diff options
Diffstat (limited to 'audio/filter')
37 files changed, 10952 insertions, 0 deletions
diff --git a/audio/filter/af.c b/audio/filter/af.c new file mode 100644 index 0000000000..1f3e446821 --- /dev/null +++ b/audio/filter/af.c @@ -0,0 +1,700 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "osdep/strsep.h" + +#include "af.h" + +// Static list of filters +extern struct af_info af_info_dummy; +extern struct af_info af_info_delay; +extern struct af_info af_info_channels; +extern struct af_info af_info_format; +extern struct af_info af_info_resample; +extern struct af_info af_info_volume; +extern struct af_info af_info_equalizer; +extern struct af_info af_info_pan; +extern struct af_info af_info_surround; +extern struct af_info af_info_sub; +extern struct af_info af_info_export; +extern struct af_info af_info_volnorm; +extern struct af_info af_info_extrastereo; +extern struct af_info af_info_lavcac3enc; +extern struct af_info af_info_lavcresample; +extern struct af_info af_info_sweep; +extern struct af_info af_info_hrtf; +extern struct af_info af_info_ladspa; +extern struct af_info af_info_center; +extern struct af_info af_info_sinesuppress; +extern struct af_info af_info_karaoke; +extern struct af_info af_info_scaletempo; +extern struct af_info af_info_bs2b; + +static struct af_info* filter_list[]={ + &af_info_dummy, + &af_info_delay, + &af_info_channels, + &af_info_format, + &af_info_resample, + &af_info_volume, + &af_info_equalizer, + &af_info_pan, + &af_info_surround, + &af_info_sub, +#ifdef HAVE_SYS_MMAN_H + &af_info_export, +#endif + &af_info_volnorm, + &af_info_extrastereo, + &af_info_lavcac3enc, + &af_info_lavcresample, + &af_info_sweep, + &af_info_hrtf, +#ifdef CONFIG_LADSPA + &af_info_ladspa, +#endif + &af_info_center, + &af_info_sinesuppress, + &af_info_karaoke, + &af_info_scaletempo, +#ifdef CONFIG_LIBBS2B + &af_info_bs2b, +#endif + NULL +}; + +// CPU speed +int* af_cpu_speed = NULL; + +/* Find a filter in the static list of filters using it's name. This + function is used internally */ +static struct af_info* 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; +} + +/* Find filter in the dynamic filter list using it's name This + function is used for finding already initialized filters */ +struct af_instance* af_get(struct af_stream* s, char* name) +{ + struct af_instance* af=s->first; + // Find the filter + while(af != NULL){ + if(!strcmp(af->info->name,name)) + return af; + af=af->next; + } + return NULL; +} + +/*/ Function for creating a new filter of type name. The name may + contain the commandline parameters for the filter */ +static struct af_instance* af_create(struct af_stream* s, const char* name_with_cmd) +{ + char* name = strdup(name_with_cmd); + char* cmdline = name; + + // Allocate space for the new filter and reset all pointers + struct af_instance* new=malloc(sizeof(struct af_instance)); + if (!name || !new) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Could not allocate memory\n"); + goto err_out; + } + memset(new,0,sizeof(struct af_instance)); + + // Check for commandline parameters + strsep(&cmdline, "="); + + // Find filter from name + if(NULL == (new->info=af_find(name))) + goto err_out; + + /* Make sure that the filter is not already in the list if it is + non-reentrant */ + if(new->info->flags & AF_FLAGS_NOT_REENTRANT){ + if(af_get(s,name)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] There can only be one instance of" + " the filter '%s' in each stream\n",name); + goto err_out; + } + } + + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Adding filter %s \n",name); + + // Initialize the new filter + if(AF_OK == new->info->open(new) && + AF_ERROR < new->control(new,AF_CONTROL_POST_CREATE,&s->cfg)){ + if(cmdline){ + if(AF_ERROR>=new->control(new,AF_CONTROL_COMMAND_LINE,cmdline)) + goto err_out; + } + free(name); + return new; + } + +err_out: + free(new); + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Couldn't create or open audio filter '%s'\n", + name); + free(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 */ +static struct af_instance* af_prepend(struct af_stream* s, struct af_instance* af, const char* name) +{ + // Create the new filter and make sure it is OK + struct af_instance* new=af_create(s,name); + if(!new) + return NULL; + // Update pointers + new->next=af; + if(af){ + new->prev=af->prev; + af->prev=new; + } + else + s->last=new; + if(new->prev) + new->prev->next=new; + else + s->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 */ +static struct af_instance* af_append(struct af_stream* s, struct af_instance* af, const char* name) +{ + // Create the new filter and make sure it is OK + struct af_instance* new=af_create(s,name); + if(!new) + return NULL; + // Update pointers + new->prev=af; + if(af){ + new->next=af->next; + af->next=new; + } + else + s->first=new; + if(new->next) + new->next->prev=new; + else + s->last=new; + return new; +} + +// Uninit and remove the filter "af" +void af_remove(struct af_stream* s, struct af_instance* af) +{ + if(!af) return; + + // Print friendly message + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Removing filter %s \n",af->info->name); + + // Notify filter before changing anything + af->control(af,AF_CONTROL_PRE_DESTROY,0); + + // Detach pointers + if(af->prev) + af->prev->next=af->next; + else + s->first=af->next; + if(af->next) + af->next->prev=af->prev; + else + s->last=af->prev; + + // Uninitialize af and free memory + af->uninit(af); + free(af); +} + +static void print_fmt(struct mp_audio *d) +{ + if (d) { + mp_msg(MSGT_AFILTER, MSGL_V, "%dHz/%dch/%s", d->rate, d->nch, + af_fmt2str_short(d->format)); + } else { + mp_msg(MSGT_AFILTER, MSGL_V, "(?)"); + } +} + +static void af_print_filter_chain(struct af_stream* s) +{ + mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n"); + + mp_msg(MSGT_AFILTER, MSGL_V, " [in] "); + print_fmt(&s->input); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + struct af_instance *af = s->first; + while (af) { + mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name); + print_fmt(af->data); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + af = af->next; + } + + mp_msg(MSGT_AFILTER, MSGL_V, " [out] "); + print_fmt(&s->output); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); +} + +// Warning: +// A failed af_reinit() leaves the audio chain behind in a useless, broken +// state (for example, format filters that were tentatively inserted stay +// inserted). +// In that case, you should always rebuild the filter chain, or abort. +int af_reinit(struct af_stream* s, struct af_instance* af) +{ + do{ + struct mp_audio in; // Format of the input to current filter + int rv=0; // Return value + + // Check if there are any filters left in the list + if(NULL == af){ + if(!(af=af_append(s,s->first,"dummy"))) + return AF_UNKNOWN; + else + return AF_ERROR; + } + + // Check if this is the first filter + if(!af->prev) + memcpy(&in,&(s->input),sizeof(struct mp_audio)); + else + memcpy(&in,af->prev->data,sizeof(struct mp_audio)); + // Reset just in case... + in.audio=NULL; + in.len=0; + + rv = af->control(af,AF_CONTROL_REINIT,&in); + switch(rv){ + case AF_OK: + af = af->next; + break; + case AF_FALSE:{ // Configuration filter is needed + // Do auto insertion only if force is not specified + if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){ + struct af_instance* new = NULL; + // Insert channels filter + if((af->prev?af->prev->data->nch:s->input.nch) != in.nch){ + // Create channels filter + if(NULL == (new = af_prepend(s,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,&(s->input),sizeof(struct mp_audio)); + else + memcpy(&in,new->prev->data,sizeof(struct mp_audio)); + if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in))) + return rv; + } + // Insert format filter + if((af->prev?af->prev->data->format:s->input.format) != in.format){ + // Create format filter + if(NULL == (new = af_prepend(s,af,"format"))) + return AF_ERROR; + // Set output bits per sample + in.format |= af_bits2fmt(in.bps*8); + if(AF_OK != (rv = new->control(new,AF_CONTROL_FORMAT_FMT,&in.format))) + return rv; + // Initialize format filter + if(!new->prev) + memcpy(&in,&(s->input),sizeof(struct mp_audio)); + else + memcpy(&in,new->prev->data,sizeof(struct mp_audio)); + if(AF_OK != (rv = new->control(new,AF_CONTROL_REINIT,&in))) + return rv; + } + if(!new){ // Should _never_ happen + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. " + "This error should never occur, please send a bug report.\n"); + return AF_ERROR; + } + af=new->next; + } + else { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Automatic filter insertion disabled " + "but formats do not match. Giving up.\n"); + return AF_ERROR; + } + break; + } + case AF_DETACH:{ // Filter is redundant and wants to be unloaded + // Do auto remove only if force is not specified + if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){ + struct af_instance* aft=af->prev; + af_remove(s,af); + if(aft) + af=aft->next; + else + af=s->first; // Restart configuration + } + break; + } + default: + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Reinitialization did not work, audio" + " filter '%s' returned error code %i\n",af->info->name,rv); + return AF_ERROR; + } + }while(af); + + af_print_filter_chain(s); + + return AF_OK; +} + +// Uninit and remove all filters +void af_uninit(struct af_stream* s) +{ + while(s->first) + af_remove(s,s->first); +} + +/** + * Extend the filter chain so we get the required output format at the end. + * \return AF_ERROR on error, AF_OK if successful. + */ +static int fixup_output_format(struct af_stream* s) +{ + struct af_instance* af = NULL; + // Check number of output channels fix if not OK + // If needed always inserted last -> easy to screw up other filters + if(s->output.nch && s->last->data->nch!=s->output.nch){ + if(!strcmp(s->last->info->name,"format")) + af = af_prepend(s,s->last,"channels"); + else + af = af_append(s,s->last,"channels"); + // Init the new filter + if(!af || (AF_OK != af->control(af,AF_CONTROL_CHANNELS,&(s->output.nch)))) + return AF_ERROR; + if(AF_OK != af_reinit(s,af)) + return AF_ERROR; + } + + // Check output format fix if not OK + if(s->output.format != AF_FORMAT_UNKNOWN && + s->last->data->format != s->output.format){ + if(strcmp(s->last->info->name,"format")) + af = af_append(s,s->last,"format"); + else + af = s->last; + // Init the new filter + s->output.format |= af_bits2fmt(s->output.bps*8); + if(!af || (AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT,&(s->output.format)))) + return AF_ERROR; + if(AF_OK != af_reinit(s,af)) + return AF_ERROR; + } + + // Re init again just in case + if(AF_OK != af_reinit(s,s->first)) + return AF_ERROR; + + if (s->output.format == AF_FORMAT_UNKNOWN) + s->output.format = s->last->data->format; + if (!s->output.nch) s->output.nch = s->last->data->nch; + if (!s->output.rate) s->output.rate = s->last->data->rate; + if((s->last->data->format != s->output.format) || + (s->last->data->nch != s->output.nch) || + (s->last->data->rate != s->output.rate)) { + return AF_ERROR; + } + return AF_OK; +} + +/** + * Automatic downmix to stereo in case the codec does not implement it. + */ +static void af_downmix(struct af_stream* s) +{ + static const char * const downmix_strs[AF_NCH + 1] = { + /* FL FR RL RR FC LF AL AR */ + [3] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0.4", + [4] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0:" "0:0.4", + [5] = "pan=2:" "0.5:0:" "0:0.5:" "0.2:0:" "0:0.2:" "0.3:0.3", + [6] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0.1", + [7] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0:" "0:0.1", + [8] = "pan=2:" "0.4:0:" "0:0.4:" "0.15:0:" "0:0.15:" "0.25:0.25:" "0.1:0.1:" "0.1:0:" "0:0.1", + }; + const char *af_pan_str = downmix_strs[s->input.nch]; + + if (af_pan_str) + af_append(s, s->first, af_pan_str); +} + +/* Initialize the stream "s". This function creates a new filter list + if necessary according to the values set in input and output. Input + and output should contain the format of the current movie and the + formate of the preferred output respectively. The function is + reentrant i.e. if called with an already initialized stream the + stream will be reinitialized. + If one of the prefered output parameters is 0 the one that needs + no conversion is used (i.e. the output format in the last filter). + The return value is 0 if success and -1 if failure */ +int af_init(struct af_stream* s) +{ + struct MPOpts *opts = s->opts; + int i=0; + + // Sanity check + if(!s) return -1; + + // Precaution in case caller is misbehaving + s->input.audio = s->output.audio = NULL; + s->input.len = s->output.len = 0; + + // Figure out how fast the machine is + if(AF_INIT_AUTO == (AF_INIT_TYPE_MASK & s->cfg.force)) + s->cfg.force = (s->cfg.force & ~AF_INIT_TYPE_MASK) | AF_INIT_TYPE; + + // Check if this is the first call + if(!s->first){ + // Append a downmix pan filter at the beginning of the chain if needed + if (s->input.nch != opts->audio_output_channels + && opts->audio_output_channels == 2) + af_downmix(s); + // Add all filters in the list (if there are any) + if (s->cfg.list) { + while(s->cfg.list[i]){ + if(!af_append(s,s->last,s->cfg.list[i++])) + return -1; + } + } + } + + // If we do not have any filters otherwise + // add dummy to make automatic format conversion work + if (!s->first && !af_append(s, s->first, "dummy")) + return -1; + + // Init filters + if(AF_OK != af_reinit(s,s->first)) + return -1; + + // make sure the chain is not empty and valid (e.g. because of AF_DETACH) + if (!s->first) + if (!af_append(s,s->first,"dummy") || AF_OK != af_reinit(s,s->first)) + return -1; + + // Check output format + if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){ + struct af_instance* af = NULL; // New filter + // Check output frequency if not OK fix with resample + if(s->output.rate && s->last->data->rate!=s->output.rate){ + // try to find a filter that can change samplrate + af = af_control_any_rev(s, AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET, + &(s->output.rate)); + if (!af) { + char *resampler = "resample"; + if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW) + resampler = "lavcresample"; + if((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_SLOW){ + if(!strcmp(s->first->info->name,"format")) + af = af_append(s,s->first,resampler); + else + af = af_prepend(s,s->first,resampler); + } + else{ + if(!strcmp(s->last->info->name,"format")) + af = af_prepend(s,s->last,resampler); + else + af = af_append(s,s->last,resampler); + } + // Init the new filter + if(!af || (AF_OK != af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET, + &(s->output.rate)))) + return -1; + // Use lin int if the user wants fast + if ((AF_INIT_TYPE_MASK & s->cfg.force) == AF_INIT_FAST) { + char args[32]; + sprintf(args, "%d", s->output.rate); + if (strcmp(resampler, "lavcresample") == 0) + strcat(args, ":1"); + else + strcat(args, ":0:0"); + af->control(af, AF_CONTROL_COMMAND_LINE, args); + } + } + if(AF_OK != af_reinit(s,af)) + return -1; + } + if (AF_OK != fixup_output_format(s)) { + // Something is stuffed audio out will not work + mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to setup filter system can not" + " meet sound-card demands, please send a bug report. \n"); + af_uninit(s); + return -1; + } + } + return 0; +} + +/* Add filter during execution. This function adds the filter "name" + to the stream s. The filter will be inserted somewhere nice in the + list of filters. The return value is a pointer to the new filter, + If the filter couldn't be added the return value is NULL. */ +struct af_instance* af_add(struct af_stream* s, char* name){ + struct af_instance* new; + // Sanity check + if(!s || !s->first || !name) + return NULL; + // Insert the filter somewhere nice + if(!strcmp(s->first->info->name,"format")) + new = af_append(s, s->first, name); + else + new = af_prepend(s, s->first, name); + if(!new) + return NULL; + + // Reinitalize the filter list + if(AF_OK != af_reinit(s, s->first) || + AF_OK != fixup_output_format(s)){ + while (s->first) + af_remove(s, s->first); + af_init(s); + return NULL; + } + return new; +} + +// Filter data chunk through the filters in the list +struct mp_audio* af_play(struct af_stream* s, struct mp_audio* data) +{ + struct af_instance* af=s->first; + // Iterate through all filters + do{ + if (data->len <= 0) break; + data=af->play(af,data); + af=af->next; + }while(af && data); + return data; +} + +/* Calculate the minimum output buffer size for given input data d + * when using the RESIZE_LOCAL_BUFFER macro. The +t+1 part ensures the + * value is >= len*mul rounded upwards to whole samples even if the + * double 'mul' is inexact. */ +int af_lencalc(double mul, struct mp_audio* d) +{ + int t = d->bps * d->nch; + return d->len * mul + t + 1; +} + +// Calculate average ratio of filter output size to input size +double af_calc_filter_multiplier(struct af_stream* s) +{ + struct af_instance* af=s->first; + double mul = 1; + // Iterate through all filters and calculate total multiplication factor + do{ + mul *= af->mul; + af=af->next; + }while(af); + + return mul; +} + +/* Calculate the total delay [bytes output] caused by the filters */ +double af_calc_delay(struct af_stream* s) +{ + struct af_instance* af=s->first; + register double delay = 0.0; + // Iterate through all filters + while(af){ + delay += af->delay; + delay *= af->mul; + af=af->next; + } + return delay; +} + +/* Helper function called by the macro with the same name this + function should not be called directly */ +int af_resize_local_buffer(struct af_instance* af, struct mp_audio* data) +{ + // Calculate new length + register int len = af_lencalc(af->mul,data); + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] 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 + 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_FATAL, "[libaf] Could not allocate memory \n"); + return AF_ERROR; + } + af->data->len=len; + return AF_OK; +} + +// documentation in af.h +struct af_instance *af_control_any_rev (struct af_stream* s, int cmd, void* arg) { + int res = AF_UNKNOWN; + struct af_instance* filt = s->last; + while (filt) { + res = filt->control(filt, cmd, arg); + if (res == AF_OK) + return filt; + filt = filt->prev; + } + return NULL; +} + +void af_help (void) { + int i = 0; + mp_msg(MSGT_AFILTER, MSGL_INFO, "Available audio filters:\n"); + while (filter_list[i]) { + if (filter_list[i]->comment && filter_list[i]->comment[0]) + mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s (%s)\n", filter_list[i]->name, filter_list[i]->info, filter_list[i]->comment); + else + mp_msg(MSGT_AFILTER, MSGL_INFO, " %-15s: %s\n", filter_list[i]->name, filter_list[i]->info); + i++; + } +} + +void af_fix_parameters(struct mp_audio *data) +{ + if (data->nch < 0 || data->nch > AF_NCH) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "Invalid number of channels %i, assuming 2.\n", data->nch); + data->nch = 2; + } + data->bps = af_fmt2bits(data->format)/8; +} diff --git a/audio/filter/af.h b/audio/filter/af.h new file mode 100644 index 0000000000..edce49a978 --- /dev/null +++ b/audio/filter/af.h @@ -0,0 +1,349 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AF_H +#define MPLAYER_AF_H + +#include <stdio.h> + +#include "config.h" + +#include "options.h" +#include "libaf/format.h" +#include "control.h" +#include "cpudetect.h" +#include "mp_msg.h" + +struct af_instance; + +// Number of channels +#ifndef AF_NCH +#define AF_NCH 8 +#endif + +// Audio data chunk +struct mp_audio { + 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 +}; + + +// Flags used for defining the behavior of an audio filter +#define AF_FLAGS_REENTRANT 0x00000000 +#define AF_FLAGS_NOT_REENTRANT 0x00000001 + +/* Audio filter information not specific for current instance, but for + a specific filter */ +struct af_info { + const char *info; + const char *name; + const char *author; + const char *comment; + const int flags; + int (*open)(struct af_instance *vf); +}; + +// Linked list of audio filters +struct af_instance { + struct af_info *info; + int (*control)(struct af_instance *af, int cmd, void *arg); + void (*uninit)(struct af_instance *af); + struct mp_audio * (*play)(struct af_instance *af, struct mp_audio *data); + void *setup; // setup data for this specific instance and filter + struct mp_audio *data; // configuration for outgoing data stream + struct af_instance *next; + struct af_instance *prev; + double delay; /* Delay caused by the filter, in units of bytes read without + * corresponding output */ + double mul; /* length multiplier: how much does this instance change + the length of the buffer. */ +}; + +// Initialization flags +extern int *af_cpu_speed; + +#define AF_INIT_AUTO 0x00000000 +#define AF_INIT_SLOW 0x00000001 +#define AF_INIT_FAST 0x00000002 +#define AF_INIT_FORCE 0x00000003 +#define AF_INIT_TYPE_MASK 0x00000003 + +#define AF_INIT_INT 0x00000000 +#define AF_INIT_FLOAT 0x00000004 +#define AF_INIT_FORMAT_MASK 0x00000004 + +// Default init type +#ifndef AF_INIT_TYPE +#define AF_INIT_TYPE (af_cpu_speed ? *af_cpu_speed : AF_INIT_SLOW) +#endif + +// Configuration switches +struct af_cfg { + int force; // Initialization type + char **list; /* list of names of filters that are added to filter + list during first initialization of stream */ +}; + +// Current audio stream +struct af_stream { + // The first and last filter in the list + struct af_instance *first; + struct af_instance *last; + // Storage for input and output data formats + struct mp_audio input; + struct mp_audio output; + // Configuration for this stream + struct af_cfg cfg; + struct MPOpts *opts; +}; + +/********************************************* + // 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_FATAL -3 + + + +/********************************************* + // Export functions + */ + +/** + * \defgroup af_chain Audio filter chain functions + * \{ + * \param s filter chain + */ + +/** + * \brief Initialize the stream "s". + * \return 0 on success, -1 on failure + * + * This function creates a new filter list if necessary, according + * to the values set in input and output. Input and output should contain + * the format of the current movie and the format of the preferred output + * respectively. + * Filters to convert to the preferred output format are inserted + * automatically, except when they are set to 0. + * The function is reentrant i.e. if called with an already initialized + * stream the stream will be reinitialized. + */ +int af_init(struct af_stream *s); + +/** + * \brief Uninit and remove all filters from audio filter chain + */ +void af_uninit(struct af_stream *s); + +/** + * \brief Reinit the filter list from the given filter on downwards + * \param Filter instance to begin the reinit from + * \return AF_OK on success or AF_ERROR on failure + */ +int af_reinit(struct af_stream *s, struct af_instance *af); + +/** + * \brief This function adds the filter "name" to the stream s. + * \param name name of filter to add + * \return pointer to the new filter, NULL if insert failed + * + * The filter will be inserted somewhere nice in the + * list of filters (i.e. at the beginning unless the + * first filter is the format filter (why??). + */ +struct af_instance *af_add(struct af_stream *s, char *name); + +/** + * \brief Uninit and remove the filter "af" + * \param af filter to remove + */ +void af_remove(struct af_stream *s, struct af_instance *af); + +/** + * \brief find filter in chain by name + * \param name name of the filter to find + * \return first filter with right name or NULL if not found + * + * This function is used for finding already initialized filters + */ +struct af_instance *af_get(struct af_stream *s, char *name); + +/** + * \brief filter data chunk through the filters in the list + * \param data data to play + * \return resulting data + * \ingroup af_chain + */ +struct mp_audio *af_play(struct af_stream *s, struct mp_audio *data); + +/** + * \brief send control to all filters, starting with the last until + * one accepts the command with AF_OK. + * \param cmd filter control command + * \param arg argument for filter command + * \return the accepting filter or NULL if none was found + */ +struct af_instance *af_control_any_rev(struct af_stream *s, int cmd, void *arg); + +/** + * \brief calculate average ratio of filter output lenth to input length + * \return the ratio + */ +double af_calc_filter_multiplier(struct af_stream *s); + +/** + * \brief Calculate the total delay caused by the filters + * \return delay in bytes of "missing" output + */ +double af_calc_delay(struct af_stream *s); + +/** \} */ // end of af_chain group + +// Helper functions and macros used inside the audio filters + +/** + * \defgroup af_filter Audio filter helper functions + * \{ + */ + +/* Helper function called by the macro with the same name only to be + called from inside filters */ +int af_resize_local_buffer(struct af_instance *af, struct mp_audio *data); + +/* Helper function used to calculate the exact buffer length needed + when buffers are resized. The returned length is >= than what is + needed */ +int af_lencalc(double mul, struct mp_audio *data); + +/** + * \brief convert dB to gain value + * \param n number of values to convert + * \param in [in] values in dB, <= -200 will become 0 gain + * \param out [out] gain values + * \param k input values are divided by this + * \param mi minimum dB value, input will be clamped to this + * \param ma maximum dB value, input will be clamped to this + * \return AF_ERROR on error, AF_OK otherwise + */ +int af_from_dB(int n, float *in, float *out, float k, float mi, float ma); + +/** + * \brief convert gain value to dB + * \param n number of values to convert + * \param in [in] gain values, 0 wil become -200 dB + * \param out [out] values in dB + * \param k output values will be multiplied by this + * \return AF_ERROR on error, AF_OK otherwise + */ +int af_to_dB(int n, float *in, float *out, float k); + +/** + * \brief convert milliseconds to sample time + * \param n number of values to convert + * \param in [in] values in milliseconds + * \param out [out] sample time values + * \param rate sample rate + * \param mi minimum ms value, input will be clamped to this + * \param ma maximum ms value, input will be clamped to this + * \return AF_ERROR on error, AF_OK otherwise + */ +int af_from_ms(int n, float *in, int *out, int rate, float mi, float ma); + +/** + * \brief convert sample time to milliseconds + * \param n number of values to convert + * \param in [in] sample time values + * \param out [out] values in milliseconds + * \param rate sample rate + * \return AF_ERROR on error, AF_OK otherwise + */ +int af_to_ms(int n, int *in, float *out, int rate); + +/** + * \brief test if output format matches + * \param af audio filter + * \param out needed format, will be overwritten by available + * format if they do not match + * \return AF_FALSE if formats do not match, AF_OK if they match + * + * compares the format, bps, rate and nch values of af->data with out + */ +int af_test_output(struct af_instance *af, struct mp_audio *out); + +/** + * \brief soft clipping function using sin() + * \param a input value + * \return clipped value + */ +float af_softclip(float a); + +/** \} */ // end of af_filter group, but more functions of this group below + +/** Print a list of all available audio filters */ +void af_help(void); + +/** + * \brief fill the missing parameters in the struct mp_audio structure + * \param data structure to fill + * \ingroup af_filter + * + * Currently only sets bps based on format + */ +void af_fix_parameters(struct mp_audio *data); + +/** 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. + * \ingroup af_filter + */ +#define RESIZE_LOCAL_BUFFER(a, d) \ + ((a->data->len < \ + af_lencalc(a->mul, d)) ? af_resize_local_buffer(a, d) : AF_OK) + +/* Some other useful macro definitions*/ +#ifndef min +#define min(a, b)(((a) > (b)) ? (b) : (a)) +#endif + +#ifndef max +#define max(a, b)(((a) > (b)) ? (a) : (b)) +#endif + +#ifndef clamp +#define clamp(a, min, max) (((a) > (max)) ? (max) : (((a) < (min)) ? (min) : (a))) +#endif + +#ifndef sign +#define sign(a) (((a) > 0) ? (1) : (-1)) +#endif + +#ifndef lrnd +#define lrnd(a, b) ((b)((a) >= 0.0 ? (a) + 0.5 : (a) - 0.5)) +#endif + +#endif /* MPLAYER_AF_H */ diff --git a/audio/filter/af_bs2b.c b/audio/filter/af_bs2b.c new file mode 100644 index 0000000000..ccbf3794c5 --- /dev/null +++ b/audio/filter/af_bs2b.c @@ -0,0 +1,274 @@ +/* + * The Bauer stereophonic-to-binaural DSP using bs2b library: + * http://bs2b.sourceforge.net/ + * + * Copyright (c) 2009 Andrew Savchenko + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <bs2b.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> + +#include "af.h" +#include "subopt-helper.h" + +/// Internal specific data of the filter +struct af_bs2b { + int fcut; ///< cut frequency in Hz + int feed; ///< feed level for low frequencies in 0.1*dB + char *profile; ///< profile (available crossfeed presets) + t_bs2bdp filter; ///< instance of a library filter +}; + +#define PLAY(name, type) \ +static struct mp_audio *play_##name(struct af_instance *af, struct mp_audio *data) \ +{ \ + /* filter is called for all pairs of samples available in the buffer */ \ + bs2b_cross_feed_##name(((struct af_bs2b*)(af->setup))->filter, \ + (type*)(data->audio), data->len/data->bps/2); \ +\ + return data; \ +} + +PLAY(f, float) +PLAY(fbe, float) +PLAY(fle, float) +PLAY(s32be, int32_t) +PLAY(u32be, uint32_t) +PLAY(s32le, int32_t) +PLAY(u32le, uint32_t) +PLAY(s24be, bs2b_int24_t) +PLAY(u24be, bs2b_uint24_t) +PLAY(s24le, bs2b_int24_t) +PLAY(u24le, bs2b_uint24_t) +PLAY(s16be, int16_t) +PLAY(u16be, uint16_t) +PLAY(s16le, int16_t) +PLAY(u16le, uint16_t) +PLAY(s8, int8_t) +PLAY(u8, uint8_t) + +/// Sanity check for fcut value +static int test_fcut(void *par) +{ + const int val = *(int*)par; + if (val >= BS2B_MINFCUT && val <= BS2B_MAXFCUT) + return 1; + + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[bs2b] Cut frequency must be in range [%d..%d], but current value is %d.\n", + BS2B_MINFCUT, BS2B_MAXFCUT, val); + return 0; +} + +/// Sanity check for feed value +static int test_feed(void *par) +{ + const int val = *(int*)par; + if (val >= BS2B_MINFEED && val <= BS2B_MAXFEED) + return 1; + + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[bs2b] Feed level must be in range [%d..%d], but current value is %d.\n", + BS2B_MINFEED, BS2B_MAXFEED, val); + return 0; +} + +/// Initialization and runtime control +static int control(struct af_instance *af, int cmd, void *arg) +{ + struct af_bs2b *s = af->setup; + + switch (cmd) { + case AF_CONTROL_REINIT: { + int format; + char buf[256]; + // Sanity check + if (!arg) return AF_ERROR; + + format = ((struct mp_audio*)arg)->format; + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = 2; // bs2b is useful only for 2ch audio + af->data->bps = ((struct mp_audio*)arg)->bps; + af->data->format = format; + + /* check for formats supported by libbs2b + and assign corresponding handlers */ + switch (format) { + case AF_FORMAT_FLOAT_BE: + af->play = play_fbe; + break; + case AF_FORMAT_FLOAT_LE: + af->play = play_fle; + break; + case AF_FORMAT_S32_BE: + af->play = play_s32be; + break; + case AF_FORMAT_U32_BE: + af->play = play_u32be; + break; + case AF_FORMAT_S32_LE: + af->play = play_s32le; + break; + case AF_FORMAT_U32_LE: + af->play = play_u32le; + break; + case AF_FORMAT_S24_BE: + af->play = play_s24be; + break; + case AF_FORMAT_U24_BE: + af->play = play_u24be; + break; + case AF_FORMAT_S24_LE: + af->play = play_s24le; + break; + case AF_FORMAT_U24_LE: + af->play = play_u24le; + break; + case AF_FORMAT_S16_BE: + af->play = play_s16be; + break; + case AF_FORMAT_U16_BE: + af->play = play_u16be; + break; + case AF_FORMAT_S16_LE: + af->play = play_s16le; + break; + case AF_FORMAT_U16_LE: + af->play = play_u16le; + break; + case AF_FORMAT_S8: + af->play = play_s8; + break; + case AF_FORMAT_U8: + af->play = play_u8; + break; + default: + af->play = play_f; + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + break; + } + + // bs2b have srate limits, try to resample if needed + if (af->data->rate > BS2B_MAXSRATE || af->data->rate < BS2B_MINSRATE) { + af->data->rate = BS2B_DEFAULT_SRATE; + mp_msg(MSGT_AFILTER, MSGL_WARN, + "[bs2b] Requested sample rate %d Hz is out of bounds [%d..%d] Hz.\n" + "[bs2b] Trying to resample to %d Hz.\n", + af->data->rate, BS2B_MINSRATE, BS2B_MAXSRATE, BS2B_DEFAULT_SRATE); + } + bs2b_set_srate(s->filter, (long)af->data->rate); + mp_msg(MSGT_AFILTER, MSGL_V, "[bs2b] using format %s\n", + af_fmt2str(af->data->format,buf,256)); + + return af_test_output(af,(struct mp_audio*)arg); + } + case AF_CONTROL_COMMAND_LINE: { + const opt_t subopts[] = { + {"fcut", OPT_ARG_INT, &s->fcut, test_fcut}, + {"feed", OPT_ARG_INT, &s->feed, test_feed}, + {"profile", OPT_ARG_MSTRZ, &s->profile, NULL}, + {NULL} + }; + if (subopt_parse(arg, subopts) != 0) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[bs2b] Invalid option specified.\n"); + free(s->profile); + return AF_ERROR; + } + // parse profile if specified + if (s->profile) { + if (!strcmp(s->profile, "default")) + bs2b_set_level(s->filter, BS2B_DEFAULT_CLEVEL); + else if (!strcmp(s->profile, "cmoy")) + bs2b_set_level(s->filter, BS2B_CMOY_CLEVEL); + else if (!strcmp(s->profile, "jmeier")) + bs2b_set_level(s->filter, BS2B_JMEIER_CLEVEL); + else { + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[bs2b] Invalid profile specified: %s.\n" + "[bs2b] Available profiles are: default, cmoy, jmeier.\n", + s->profile); + free(s->profile); + return AF_ERROR; + } + } + // set fcut and feed only if specified, otherwise defaults will be used + if (s->fcut) + bs2b_set_level_fcut(s->filter, s->fcut); + if (s->feed) + bs2b_set_level_feed(s->filter, s->feed); + + mp_msg(MSGT_AFILTER, MSGL_V, + "[bs2b] using cut frequency %d, LF feed level %d\n", + bs2b_get_level_fcut(s->filter), bs2b_get_level_feed(s->filter)); + free(s->profile); + return AF_OK; + } + } + return AF_UNKNOWN; +} + +/// Deallocate memory and close library +static void uninit(struct af_instance *af) +{ + struct af_bs2b *s = af->setup; + free(af->data); + if (s && s->filter) + bs2b_close(s->filter); + free(s); +} + +/// Allocate memory, set function pointers and init library +static int af_open(struct af_instance *af) +{ + struct af_bs2b *s; + af->control = control; + af->uninit = uninit; + af->mul = 1; + if (!(af->data = calloc(1, sizeof(struct mp_audio)))) + return AF_ERROR; + if (!(af->setup = s = calloc(1, sizeof(struct af_bs2b)))) { + free(af->data); + return AF_ERROR; + } + + // NULL means failed initialization + if (!(s->filter = bs2b_open())) { + free(af->data); + free(af->setup); + return AF_ERROR; + } + // Set zero defaults indicating no option was specified. + s->profile = NULL; + s->fcut = 0; + s->feed = 0; + return AF_OK; +} + +/// Description of this filter +struct af_info af_info_bs2b = { + "Bauer stereophonic-to-binaural audio filter", + "bs2b", + "Andrew Savchenko", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_center.c b/audio/filter/af_center.c new file mode 100644 index 0000000000..aa9aae8514 --- /dev/null +++ b/audio/filter/af_center.c @@ -0,0 +1,129 @@ +/* + * This filter adds a center channel to the audio stream by + * averaging the left and right channel. + * There are two runtime controls one for setting which channel + * to insert the center-audio into called AF_CONTROL_SUB_CH. + * + * FIXME: implement a high-pass filter for better results. + * + * copyright (c) 2005 Alex Beregszaszi + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_center_s +{ + int ch; // Channel number which to insert the filtered data +}af_center_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_center_t* s = af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT:{ + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch); + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + + return af_test_output(af,(struct mp_audio*)arg); + } + case AF_CONTROL_COMMAND_LINE:{ + int ch=1; + sscanf(arg,"%i", &ch); + return control(af,AF_CONTROL_CENTER_CH | AF_CONTROL_SET, &ch); + } + case AF_CONTROL_CENTER_CH | AF_CONTROL_SET: // Requires reinit + // Sanity check + if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Center channel number must be between " + " 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg); + return AF_ERROR; + } + s->ch = *(int*)arg; + return AF_OK; + case AF_CONTROL_CENTER_CH | AF_CONTROL_GET: + *(int*)arg = s->ch; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + af_center_t* s = af->setup; // Setup for this instance + float* a = c->audio; // Audio data + int len = c->len/4; // Number of samples in current audio block + int nch = c->nch; // Number of channels + int ch = s->ch; // Channel in which to insert the center audio + register int i; + + // Run filter + for(i=0;i<len;i+=nch){ + // Average left and right + a[i+ch] = (a[i]/2) + (a[i+1]/2); + } + + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af_center_t* s; + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=s=calloc(1,sizeof(af_center_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + // Set default values + s->ch = 1; // Channel nr 2 + return AF_OK; +} + +// Description of this filter +struct af_info af_info_center = { + "Audio filter for adding a center channel", + "center", + "Alex Beregszaszi", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_channels.c b/audio/filter/af_channels.c new file mode 100644 index 0000000000..8f676d8cfd --- /dev/null +++ b/audio/filter/af_channels.c @@ -0,0 +1,306 @@ +/* + * 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. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "af.h" + +#define FR 0 +#define TO 1 + +typedef struct af_channels_s{ + int route[AF_NCH][2]; + int nr; + int router; +}af_channels_t; + +// Local function for copying data +static 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 3:{ + int8_t* tin = (int8_t*)in; + int8_t* tout = (int8_t*)out; + tin += 3 * inos; + tout += 3 * outos; + len = len / ( 3 * ins); + while (len--) { + tout[0] = tin[0]; + tout[1] = tin[1]; + tout[2] = tin[2]; + tin += 3 * ins; + tout += 3 * 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); + } +} + +// Make sure the routes are sane +static int check_routes(af_channels_t* s, int nin, int nout) +{ + int i; + if((s->nr < 1) || (s->nr > AF_NCH)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be" + " between 1 and %i. Current value is %i\n",AF_NCH,s->nr); + return AF_ERROR; + } + + for(i=0;i<s->nr;i++){ + if((s->route[i][FR] >= nin) || (s->route[i][TO] >= nout)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] Invalid routing in pair nr. %i.\n", i); + return AF_ERROR; + } + } + return AF_OK; +} + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_channels_t* s = af->setup; + switch(cmd){ + case AF_CONTROL_REINIT: + + // Set default channel assignment + if(!s->router){ + int i; + // Make sure this filter isn't redundant + if(af->data->nch == ((struct mp_audio*)arg)->nch) + return AF_DETACH; + + // If mono: fake stereo + if(((struct mp_audio*)arg)->nch == 1){ + s->nr = min(af->data->nch,2); + for(i=0;i<s->nr;i++){ + s->route[i][FR] = 0; + s->route[i][TO] = i; + } + } + else{ + s->nr = min(af->data->nch, ((struct mp_audio*)arg)->nch); + for(i=0;i<s->nr;i++){ + s->route[i][FR] = i; + s->route[i][TO] = i; + } + } + } + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->format = ((struct mp_audio*)arg)->format; + af->data->bps = ((struct mp_audio*)arg)->bps; + af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch; + return check_routes(s,((struct mp_audio*)arg)->nch,af->data->nch); + case AF_CONTROL_COMMAND_LINE:{ + int nch = 0; + int n = 0; + // Check number of channels and number of routing pairs + sscanf(arg, "%i:%i%n", &nch, &s->nr, &n); + + // If router scan commandline for routing pairs + if(s->nr){ + char* cp = &((char*)arg)[n]; + int ch = 0; + // Sanity check + if((s->nr < 1) || (s->nr > AF_NCH)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of routing pairs must be" + " between 1 and %i. Current value is %i\n",AF_NCH,s->nr); + } + s->router = 1; + // Scan for pairs on commandline + while((*cp == ':') && (ch < s->nr)){ + sscanf(cp, ":%i:%i%n" ,&s->route[ch][FR], &s->route[ch][TO], &n); + mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Routing from channel %i to" + " channel %i\n",s->route[ch][FR],s->route[ch][TO]); + cp = &cp[n]; + ch++; + } + } + + if(AF_OK != af->control(af,AF_CONTROL_CHANNELS | AF_CONTROL_SET ,&nch)) + return AF_ERROR; + return AF_OK; + } + case AF_CONTROL_CHANNELS | AF_CONTROL_SET: + // Reinit must be called after this function has been called + + // Sanity check + if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[channels] The number of output channels must be" + " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]); + return AF_ERROR; + } + + af->data->nch=((int*)arg)[0]; + if(!s->router) + mp_msg(MSGT_AFILTER, MSGL_V, "[channels] Changing number of channels" + " to %i\n",af->data->nch); + return AF_OK; + case AF_CONTROL_CHANNELS | AF_CONTROL_GET: + *(int*)arg = af->data->nch; + return AF_OK; + case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_SET:{ + int ch = ((af_control_ext_t*)arg)->ch; + int* route = ((af_control_ext_t*)arg)->arg; + s->route[ch][FR] = route[FR]; + s->route[ch][TO] = route[TO]; + return AF_OK; + } + case AF_CONTROL_CHANNELS_ROUTING | AF_CONTROL_GET:{ + int ch = ((af_control_ext_t*)arg)->ch; + int* route = ((af_control_ext_t*)arg)->arg; + route[FR] = s->route[ch][FR]; + route[TO] = s->route[ch][TO]; + return AF_OK; + } + case AF_CONTROL_CHANNELS_NR | AF_CONTROL_SET: + s->nr = *(int*)arg; + return AF_OK; + case AF_CONTROL_CHANNELS_NR | AF_CONTROL_GET: + *(int*)arg = s->nr; + return AF_OK; + case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_SET: + s->router = *(int*)arg; + return AF_OK; + case AF_CONTROL_CHANNELS_ROUTER | AF_CONTROL_GET: + *(int*)arg = s->router; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->setup); + if (af->data) + free(af->data->audio); + free(af->data); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + struct mp_audio* l = af->data; // Local data + af_channels_t* s = af->setup; + int i; + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + // Reset unused channels + memset(l->audio,0,c->len / c->nch * l->nch); + + if(AF_OK == check_routes(s,c->nch,l->nch)) + for(i=0;i<s->nr;i++) + copy(c->audio,l->audio,c->nch,s->route[i][FR], + l->nch,s->route[i][TO],c->len,c->bps); + + // Set output data + c->audio = l->audio; + c->len = c->len / c->nch * l->nch; + c->nch = l->nch; + + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_channels_t)); + if((af->data == NULL) || (af->setup == NULL)) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_channels = { + "Insert or remove channels", + "channels", + "Anders", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_delay.c b/audio/filter/af_delay.c new file mode 100644 index 0000000000..ce8d71980b --- /dev/null +++ b/audio/filter/af_delay.c @@ -0,0 +1,200 @@ +/* + * This audio filter delays the output signal for the different + * channels and can be used for simple position panning. + * An extension for this filter would be a reverb. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "af.h" + +#define L 65536 + +#define UPDATEQI(qi) qi=(qi+1)&(L-1) + +// Data for specific instances of this filter +typedef struct af_delay_s +{ + void* q[AF_NCH]; // Circular queues used for delaying audio signal + int wi[AF_NCH]; // Write index + int ri; // Read index + float d[AF_NCH]; // Delay [ms] +}af_delay_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_delay_t* s = af->setup; + switch(cmd){ + case AF_CONTROL_REINIT:{ + int i; + + // Free prevous delay queues + for(i=0;i<af->data->nch;i++) + free(s->q[i]); + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + af->data->format = ((struct mp_audio*)arg)->format; + af->data->bps = ((struct mp_audio*)arg)->bps; + + // Allocate new delay queues + for(i=0;i<af->data->nch;i++){ + s->q[i] = calloc(L,af->data->bps); + if(NULL == s->q[i]) + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n"); + } + + return control(af,AF_CONTROL_DELAY_LEN | AF_CONTROL_SET,s->d); + } + case AF_CONTROL_COMMAND_LINE:{ + int n = 1; + int i = 0; + char* cl = arg; + while(n && i < AF_NCH ){ + sscanf(cl,"%f:%n",&s->d[i],&n); + if(n==0 || cl[n-1] == '\0') + break; + cl=&cl[n]; + i++; + } + return AF_OK; + } + case AF_CONTROL_DELAY_LEN | AF_CONTROL_SET:{ + int i; + if(AF_OK != af_from_ms(AF_NCH, arg, s->wi, af->data->rate, 0.0, 1000.0)) + return AF_ERROR; + s->ri = 0; + for(i=0;i<AF_NCH;i++){ + mp_msg(MSGT_AFILTER, MSGL_DBG2, "[delay] Channel %i delayed by %0.3fms\n", + i,clamp(s->d[i],0.0,1000.0)); + mp_msg(MSGT_AFILTER, MSGL_DBG3, "[delay] Channel %i delayed by %i samples\n", + i,s->wi[i]); + } + return AF_OK; + } + case AF_CONTROL_DELAY_LEN | AF_CONTROL_GET:{ + int i; + for(i=0;i<AF_NCH;i++){ + if(s->ri > s->wi[i]) + s->wi[i] = L - (s->ri - s->wi[i]); + else + s->wi[i] = s->wi[i] - s->ri; + } + return af_to_ms(AF_NCH, s->wi, arg, af->data->rate); + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + int i; + + free(af->data); + for(i=0;i<AF_NCH;i++) + free(((af_delay_t*)(af->setup))->q[i]); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + af_delay_t* s = af->setup; // Setup for this instance + int nch = c->nch; // Number of channels + int len = c->len/c->bps; // Number of sample in data chunk + int ri = 0; + int ch,i; + for(ch=0;ch<nch;ch++){ + switch(c->bps){ + case 1:{ + int8_t* a = c->audio; + int8_t* q = s->q[ch]; + int wi = s->wi[ch]; + ri = s->ri; + for(i=ch;i<len;i+=nch){ + q[wi] = a[i]; + a[i] = q[ri]; + UPDATEQI(wi); + UPDATEQI(ri); + } + s->wi[ch] = wi; + break; + } + case 2:{ + int16_t* a = c->audio; + int16_t* q = s->q[ch]; + int wi = s->wi[ch]; + ri = s->ri; + for(i=ch;i<len;i+=nch){ + q[wi] = a[i]; + a[i] = q[ri]; + UPDATEQI(wi); + UPDATEQI(ri); + } + s->wi[ch] = wi; + break; + } + case 4:{ + int32_t* a = c->audio; + int32_t* q = s->q[ch]; + int wi = s->wi[ch]; + ri = s->ri; + for(i=ch;i<len;i+=nch){ + q[wi] = a[i]; + a[i] = q[ri]; + UPDATEQI(wi); + UPDATEQI(ri); + } + s->wi[ch] = wi; + break; + } + } + } + s->ri = ri; + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + 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 +struct af_info af_info_delay = { + "Delay audio filter", + "delay", + "Anders", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_dummy.c b/audio/filter/af_dummy.c new file mode 100644 index 0000000000..29a5b3d4b8 --- /dev/null +++ b/audio/filter/af_dummy.c @@ -0,0 +1,76 @@ +/* + * The name speaks for itself. This filter is a dummy and will + * not blow up regardless of what you do with it. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "af.h" + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT: + memcpy(af->data,(struct mp_audio*)arg,sizeof(struct mp_audio)); + mp_msg(MSGT_AFILTER, MSGL_V, "[dummy] Was reinitialized: %iHz/%ich/%s\n", + af->data->rate,af->data->nch,af_fmt2str_short(af->data->format)); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + // Do something necessary to get rid of annoying warning during compile + if(!af) + mp_msg(MSGT_AFILTER, MSGL_ERR, "EEEK: Argument af == NULL in af_dummy.c play()."); + return data; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=malloc(sizeof(struct mp_audio)); + if(af->data == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_dummy = { + "dummy", + "dummy", + "Anders", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_equalizer.c b/audio/filter/af_equalizer.c new file mode 100644 index 0000000000..c488ffaeaf --- /dev/null +++ b/audio/filter/af_equalizer.c @@ -0,0 +1,248 @@ +/* + * Equalizer filter, implementation of a 10 band time domain graphic + * equalizer using IIR filters. The IIR filters are implemented using a + * Direct Form II approach, but has been modified (b1 == 0 always) to + * save computation. + * + * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <inttypes.h> +#include <math.h> + +#include "af.h" + +#define L 2 // Storage for filter taps +#define KM 10 // Max number of bands + +#define Q 1.2247449 /* Q value for band-pass filters 1.2247=(3/2)^(1/2) + gives 4dB suppression @ Fc*2 and Fc/2 */ + +/* Center frequencies for band-pass filters + The different frequency bands are: + nr. center frequency + 0 31.25 Hz + 1 62.50 Hz + 2 125.0 Hz + 3 250.0 Hz + 4 500.0 Hz + 5 1.000 kHz + 6 2.000 kHz + 7 4.000 kHz + 8 8.000 kHz + 9 16.00 kHz +*/ +#define CF {31.25,62.5,125,250,500,1000,2000,4000,8000,16000} + +// Maximum and minimum gain for the bands +#define G_MAX +12.0 +#define G_MIN -12.0 + +// Data for specific instances of this filter +typedef struct af_equalizer_s +{ + float a[KM][L]; // A weights + float b[KM][L]; // B weights + float wq[AF_NCH][KM][L]; // Circular buffer for W data + float g[AF_NCH][KM]; // Gain factor for each channel and band + int K; // Number of used eq bands + int channels; // Number of channels + float gain_factor; // applied at output to avoid clipping +} af_equalizer_t; + +// 2nd order Band-pass Filter design +static void bp2(float* a, float* b, float fc, float q){ + double th= 2.0 * M_PI * fc; + double C = (1.0 - tan(th*q/2.0))/(1.0 + tan(th*q/2.0)); + + a[0] = (1.0 + C) * cos(th); + a[1] = -1 * C; + + b[0] = (1.0 - C)/2.0; + b[1] = -1.0050; +} + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_equalizer_t* s = (af_equalizer_t*)af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT:{ + int k =0, i =0; + float F[KM] = CF; + + s->gain_factor=0.0; + + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + + // Calculate number of active filters + s->K=KM; + while(F[s->K-1] > (float)af->data->rate/2.2) + s->K--; + + if(s->K != KM) + mp_msg(MSGT_AFILTER, MSGL_INFO, "[equalizer] Limiting the number of filters to" + " %i due to low sample rate.\n",s->K); + + // Generate filter taps + for(k=0;k<s->K;k++) + bp2(s->a[k],s->b[k],F[k]/((float)af->data->rate),Q); + + // Calculate how much this plugin adds to the overall time delay + af->delay = 2 * af->data->nch * af->data->bps; + + // Calculate gain factor to prevent clipping at output + for(k=0;k<AF_NCH;k++) + { + for(i=0;i<KM;i++) + { + if(s->gain_factor < s->g[k][i]) s->gain_factor=s->g[k][i]; + } + } + + s->gain_factor=log10(s->gain_factor + 1.0) * 20.0; + + if(s->gain_factor > 0.0) + { + s->gain_factor=0.1+(s->gain_factor/12.0); + }else{ + s->gain_factor=1; + } + + return af_test_output(af,arg); + } + case AF_CONTROL_COMMAND_LINE:{ + float g[10]={0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}; + int i,j; + sscanf((char*)arg,"%f:%f:%f:%f:%f:%f:%f:%f:%f:%f", &g[0], &g[1], + &g[2], &g[3], &g[4], &g[5], &g[6], &g[7], &g[8] ,&g[9]); + for(i=0;i<AF_NCH;i++){ + for(j=0;j<KM;j++){ + ((af_equalizer_t*)af->setup)->g[i][j] = + pow(10.0,clamp(g[j],G_MIN,G_MAX)/20.0)-1.0; + } + } + return AF_OK; + } + case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_SET:{ + float* gain = ((af_control_ext_t*)arg)->arg; + int ch = ((af_control_ext_t*)arg)->ch; + int k; + if(ch >= AF_NCH || ch < 0) + return AF_ERROR; + + for(k = 0 ; k<KM ; k++) + s->g[ch][k] = pow(10.0,clamp(gain[k],G_MIN,G_MAX)/20.0)-1.0; + + return AF_OK; + } + case AF_CONTROL_EQUALIZER_GAIN | AF_CONTROL_GET:{ + float* gain = ((af_control_ext_t*)arg)->arg; + int ch = ((af_control_ext_t*)arg)->ch; + int k; + if(ch >= AF_NCH || ch < 0) + return AF_ERROR; + + for(k = 0 ; k<KM ; k++) + gain[k] = log10(s->g[ch][k]+1.0) * 20.0; + + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + af_equalizer_t* s = (af_equalizer_t*)af->setup; // Setup + uint32_t ci = af->data->nch; // Index for channels + uint32_t nch = af->data->nch; // Number of channels + + while(ci--){ + float* g = s->g[ci]; // Gain factor + float* in = ((float*)c->audio)+ci; + float* out = ((float*)c->audio)+ci; + float* end = in + c->len/4; // Block loop end + + while(in < end){ + register int k = 0; // Frequency band index + register float yt = *in; // Current input sample + in+=nch; + + // Run the filters + for(;k<s->K;k++){ + // Pointer to circular buffer wq + register float* wq = s->wq[ci][k]; + // Calculate output from AR part of current filter + register float w=yt*s->b[k][0] + wq[0]*s->a[k][0] + wq[1]*s->a[k][1]; + // Calculate output form MA part of current filter + yt+=(w + wq[1]*s->b[k][1])*g[k]; + // Update circular buffer + wq[1] = wq[0]; + wq[0] = w; + } + // Calculate output + *out=yt*s->gain_factor; + out+=nch; + } + } + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_equalizer_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_equalizer = { + "Equalizer audio filter", + "equalizer", + "Anders", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_export.c b/audio/filter/af_export.c new file mode 100644 index 0000000000..441ec31ac3 --- /dev/null +++ b/audio/filter/af_export.c @@ -0,0 +1,273 @@ +/* + * This audio filter exports the incoming signal to other processes + * using memory mapping. The memory mapped area contains a header: + * int nch, + * int size, + * unsigned long long counter (updated every time the contents of + * the area changes), + * the rest is payload (non-interleaved). + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <unistd.h> +#include "config.h" + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "af.h" +#include "path.h" + +#define DEF_SZ 512 // default buffer size (in samples) +#define SHARED_FILE "mpv-af_export" /* default file name + (relative to ~/.mpv/ */ + +#define SIZE_HEADER (2 * sizeof(int) + sizeof(unsigned long long)) + +// Data for specific instances of this filter +typedef struct af_export_s +{ + unsigned long long count; // Used for sync + void* buf[AF_NCH]; // Buffers for storing the data before it is exported + int sz; // Size of buffer in samples + int wi; // Write index + int fd; // File descriptor to shared memory area + char* filename; // File to export data + uint8_t *mmap_area; // MMap shared area +} af_export_t; + + +/* Initialization and runtime control + af audio filter instance + cmd control command + arg argument +*/ +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_export_t* s = af->setup; + switch (cmd){ + case AF_CONTROL_REINIT:{ + int i=0; + int mapsize; + + // Free previous buffers + if (s->buf) + free(s->buf[0]); + + // unmap previous area + if(s->mmap_area) + munmap(s->mmap_area, SIZE_HEADER + (af->data->bps*s->sz*af->data->nch)); + // close previous file descriptor + if(s->fd) + close(s->fd); + + // Accept only int16_t as input format (which sucks) + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + + // If buffer length isn't set, set it to the default value + if(s->sz == 0) + s->sz = DEF_SZ; + + // Allocate new buffers (as one continuous block) + s->buf[0] = calloc(s->sz*af->data->nch, af->data->bps); + if(NULL == s->buf[0]) + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Out of memory\n"); + for(i = 1; i < af->data->nch; i++) + s->buf[i] = (uint8_t *)s->buf[0] + i*s->sz*af->data->bps; + + // Init memory mapping + s->fd = open(s->filename, O_RDWR | O_CREAT | O_TRUNC, 0640); + mp_msg(MSGT_AFILTER, MSGL_INFO, "[export] Exporting to file: %s\n", s->filename); + if(s->fd < 0) + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Could not open/create file: %s\n", + s->filename); + + // header + buffer + mapsize = (SIZE_HEADER + (af->data->bps * s->sz * af->data->nch)); + + // grow file to needed size + for(i = 0; i < mapsize; i++){ + char null = 0; + write(s->fd, (void*) &null, 1); + } + + // mmap size + s->mmap_area = mmap(0, mapsize, PROT_READ|PROT_WRITE,MAP_SHARED, s->fd, 0); + if(s->mmap_area == NULL) + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[export] Could not mmap file %s\n", s->filename); + mp_msg(MSGT_AFILTER, MSGL_INFO, "[export] Memory mapped to file: %s (%p)\n", + s->filename, s->mmap_area); + + // Initialize header + *((int*)s->mmap_area) = af->data->nch; + *((int*)s->mmap_area + 1) = s->sz * af->data->bps * af->data->nch; + msync(s->mmap_area, mapsize, MS_ASYNC); + + // Use test_output to return FALSE if necessary + return af_test_output(af, (struct mp_audio*)arg); + } + case AF_CONTROL_COMMAND_LINE:{ + int i=0; + char *str = arg; + + if (!str){ + free(s->filename); + + s->filename = get_path(SHARED_FILE); + return AF_OK; + } + + while((str[i]) && (str[i] != ':')) + i++; + + free(s->filename); + + s->filename = calloc(i + 1, 1); + memcpy(s->filename, str, i); + s->filename[i] = 0; + + sscanf(str + i + 1, "%d", &(s->sz)); + + return af->control(af, AF_CONTROL_EXPORT_SZ | AF_CONTROL_SET, &s->sz); + } + case AF_CONTROL_EXPORT_SZ | AF_CONTROL_SET: + s->sz = * (int *) arg; + if((s->sz <= 0) || (s->sz > 2048)) + mp_msg(MSGT_AFILTER, MSGL_ERR, "[export] Buffer size must be between" + " 1 and 2048\n" ); + + return AF_OK; + case AF_CONTROL_EXPORT_SZ | AF_CONTROL_GET: + *(int*) arg = s->sz; + return AF_OK; + + } + return AF_UNKNOWN; +} + +/* Free allocated memory and clean up other stuff too. + af audio filter instance +*/ +static void uninit( struct af_instance* af ) +{ + free(af->data); + af->data = NULL; + + if(af->setup){ + af_export_t* s = af->setup; + if (s->buf) + free(s->buf[0]); + + // Free mmaped area + if(s->mmap_area) + munmap(s->mmap_area, sizeof(af_export_t)); + + if(s->fd > -1) + close(s->fd); + + free(s->filename); + + free(af->setup); + af->setup = NULL; + } +} + +/* Filter data through filter + af audio filter instance + data audio data +*/ +static struct mp_audio* play( struct af_instance* af, struct mp_audio* data ) +{ + struct mp_audio* c = data; // Current working data + af_export_t* s = af->setup; // Setup for this instance + int16_t* a = c->audio; // Incomming sound + int nch = c->nch; // Number of channels + int len = c->len/c->bps; // Number of sample in data chunk + int sz = s->sz; // buffer size (in samples) + int flag = 0; // Set to 1 if buffer is filled + + int ch, i; + + // Fill all buffers + for(ch = 0; ch < nch; ch++){ + int wi = s->wi; // Reset write index + int16_t* b = s->buf[ch]; // Current buffer + + // Copy data to export buffers + for(i = ch; i < len; i += nch){ + b[wi++] = a[i]; + if(wi >= sz){ // Don't write outside the end of the buffer + flag = 1; + break; + } + } + s->wi = wi % s->sz; + } + + // Export buffer to mmaped area + if(flag){ + // update buffer in mapped area + memcpy(s->mmap_area + SIZE_HEADER, s->buf[0], sz * c->bps * nch); + s->count++; // increment counter (to sync) + memcpy(s->mmap_area + SIZE_HEADER - sizeof(s->count), + &(s->count), sizeof(s->count)); + } + + // We don't modify data, just export it + return data; +} + +/* Allocate memory and set function pointers + af audio filter instance + returns AF_OK or AF_ERROR +*/ +static int af_open( struct af_instance* af ) +{ + af->control = control; + af->uninit = uninit; + af->play = play; + af->mul=1; + af->data = calloc(1, sizeof(struct mp_audio)); + af->setup = calloc(1, sizeof(af_export_t)); + if((af->data == NULL) || (af->setup == NULL)) + return AF_ERROR; + + ((af_export_t *)af->setup)->filename = get_path(SHARED_FILE); + + return AF_OK; +} + +// Description of this filter +struct af_info af_info_export = { + "Sound export filter", + "export", + "Anders; Gustavo Sverzut Barbieri <gustavo.barbieri@ic.unicamp.br>", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_extrastereo.c b/audio/filter/af_extrastereo.c new file mode 100644 index 0000000000..0f7fe36861 --- /dev/null +++ b/audio/filter/af_extrastereo.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <inttypes.h> +#include <math.h> +#include <limits.h> + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_extrastereo_s +{ + float mul; +}af_extrastereo_t; + +static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data); +static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data); + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_extrastereo_t* s = (af_extrastereo_t*)af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT:{ + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = 2; + if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE) + { + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + af->play = play_float; + }// else + { + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + af->play = play_s16; + } + + return af_test_output(af,(struct mp_audio*)arg); + } + case AF_CONTROL_COMMAND_LINE:{ + float f; + sscanf((char*)arg,"%f", &f); + s->mul = f; + return AF_OK; + } + case AF_CONTROL_ES_MUL | AF_CONTROL_SET: + s->mul = *(float*)arg; + return AF_OK; + case AF_CONTROL_ES_MUL | AF_CONTROL_GET: + *(float*)arg = s->mul; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data) +{ + af_extrastereo_t *s = af->setup; + register int i = 0; + int16_t *a = (int16_t*)data->audio; // Audio data + int len = data->len/2; // Number of samples + int avg, l, r; + + for (i = 0; i < len; i+=2) + { + avg = (a[i] + a[i + 1]) / 2; + + l = avg + (int)(s->mul * (a[i] - avg)); + r = avg + (int)(s->mul * (a[i + 1] - avg)); + + a[i] = clamp(l, SHRT_MIN, SHRT_MAX); + a[i + 1] = clamp(r, SHRT_MIN, SHRT_MAX); + } + + return data; +} + +static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data) +{ + af_extrastereo_t *s = af->setup; + register int i = 0; + float *a = (float*)data->audio; // Audio data + int len = data->len/4; // Number of samples + float avg, l, r; + + for (i = 0; i < len; i+=2) + { + avg = (a[i] + a[i + 1]) / 2; + + l = avg + (s->mul * (a[i] - avg)); + r = avg + (s->mul * (a[i + 1] - avg)); + + a[i] = af_softclip(l); + a[i + 1] = af_softclip(r); + } + + return data; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play_s16; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_extrastereo_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + + ((af_extrastereo_t*)af->setup)->mul = 2.5; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_extrastereo = { + "Increase difference between audio channels", + "extrastereo", + "Alex Beregszaszi & Pierre Lombard", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c new file mode 100644 index 0000000000..4ac9caaa85 --- /dev/null +++ b/audio/filter/af_format.c @@ -0,0 +1,519 @@ +/* + * This audio 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. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <limits.h> +#include <math.h> +#include <sys/types.h> + +#include "config.h" +#include "af.h" +#include "mpbswap.h" + +/* Functions used by play to convert the input audio to the correct + format */ + +/* The below includes retrieves functions for converting to and from + ulaw and alaw */ +#include "af_format_ulaw.h" +#include "af_format_alaw.h" + +// Switch endianness +static void endian(void* in, void* out, int len, int bps); +// From signed to unsigned and the other way +static void si2us(void* data, int len, int bps); +// Change the number of bits per sample +static void change_bps(void* in, void* out, int len, int inbps, int outbps); +// From float to int signed +static void float2int(float* in, void* out, int len, int bps); +// From signed int to float +static void int2float(void* in, float* out, int len, int bps); + +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data); +static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data); +static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data); +static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data); + +// Helper functions to check sanity for input arguments + +// Sanity check for bytes per sample +static int check_bps(int bps) +{ + if(bps != 4 && bps != 3 && bps != 2 && bps != 1){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] The number of bytes per sample" + " must be 1, 2, 3 or 4. Current value is %i \n",bps); + return AF_ERROR; + } + return AF_OK; +} + +// Check for unsupported formats +static int check_format(int format) +{ + char buf[256]; + switch(format & AF_FORMAT_SPECIAL_MASK){ + case(AF_FORMAT_IMA_ADPCM): + case(AF_FORMAT_MPEG2): + case(AF_FORMAT_AC3): + mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] Sample format %s not yet supported \n", + af_fmt2str(format,buf,256)); + return AF_ERROR; + } + return AF_OK; +} + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT:{ + char buf1[256]; + char buf2[256]; + struct mp_audio *data = arg; + + // Make sure this filter isn't redundant + if(af->data->format == data->format && + af->data->bps == data->bps) + return AF_DETACH; + + // Allow trivial AC3-endianness conversion + if (!AF_FORMAT_IS_AC3(af->data->format) || !AF_FORMAT_IS_AC3(data->format)) + // Check for errors in configuration + if((AF_OK != check_bps(data->bps)) || + (AF_OK != check_format(data->format)) || + (AF_OK != check_bps(af->data->bps)) || + (AF_OK != check_format(af->data->format))) + return AF_ERROR; + + mp_msg(MSGT_AFILTER, MSGL_V, "[format] Changing sample format from %s to %s\n", + af_fmt2str(data->format,buf1,256), + af_fmt2str(af->data->format,buf2,256)); + + af->data->rate = data->rate; + af->data->nch = data->nch; + af->mul = (double)af->data->bps / data->bps; + + af->play = play; // set default + + // look whether only endianness differences are there + if ((af->data->format & ~AF_FORMAT_END_MASK) == + (data->format & ~AF_FORMAT_END_MASK)) + { + mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated endianness conversion only\n"); + af->play = play_swapendian; + } + if ((data->format == AF_FORMAT_FLOAT_NE) && + (af->data->format == AF_FORMAT_S16_NE)) + { + mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n", + af_fmt2str(data->format,buf1,256), + af_fmt2str(af->data->format,buf2,256)); + af->play = play_float_s16; + } + if ((data->format == AF_FORMAT_S16_NE) && + (af->data->format == AF_FORMAT_FLOAT_NE)) + { + mp_msg(MSGT_AFILTER, MSGL_V, "[format] Accelerated %s to %s conversion\n", + af_fmt2str(data->format,buf1,256), + af_fmt2str(af->data->format,buf2,256)); + af->play = play_s16_float; + } + return AF_OK; + } + case AF_CONTROL_COMMAND_LINE:{ + int format = af_str2fmt_short(bstr0(arg)); + if (format == -1) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[format] %s is not a valid format\n", (char *)arg); + return AF_ERROR; + } + if(AF_OK != af->control(af,AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET,&format)) + return AF_ERROR; + return AF_OK; + } + case AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET:{ + // Check for errors in configuration + if(!AF_FORMAT_IS_AC3(*(int*)arg) && AF_OK != check_format(*(int*)arg)) + return AF_ERROR; + + af->data->format = *(int*)arg; + af->data->bps = af_fmt2bits(af->data->format)/8; + + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + if (af->data) + free(af->data->audio); + free(af->data); + af->setup = 0; +} + +static struct mp_audio* play_swapendian(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* l = af->data; // Local data + struct mp_audio* c = data; // Current working data + int len = c->len/c->bps; // Length in samples of current audio block + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + endian(c->audio,l->audio,len,c->bps); + + c->audio = l->audio; + c->format = l->format; + + return c; +} + +static struct mp_audio* play_float_s16(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* l = af->data; // Local data + struct mp_audio* c = data; // Current working data + int len = c->len/4; // Length in samples of current audio block + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + float2int(c->audio, l->audio, len, 2); + + c->audio = l->audio; + c->len = len*2; + c->bps = 2; + c->format = l->format; + + return c; +} + +static struct mp_audio* play_s16_float(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* l = af->data; // Local data + struct mp_audio* c = data; // Current working data + int len = c->len/2; // Length in samples of current audio block + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + int2float(c->audio, l->audio, len, 2); + + c->audio = l->audio; + c->len = len*4; + c->bps = 4; + c->format = l->format; + + return c; +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* l = af->data; // Local data + struct mp_audio* c = data; // Current working data + int len = c->len/c->bps; // Length in samples of current audio block + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + // Change to cpu native endian format + if((c->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE) + endian(c->audio,c->audio,len,c->bps); + + // Conversion table + if((c->format & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_MU_LAW) { + from_ulaw(c->audio, l->audio, len, l->bps, l->format&AF_FORMAT_POINT_MASK); + if(AF_FORMAT_A_LAW == (l->format&AF_FORMAT_SPECIAL_MASK)) + to_ulaw(l->audio, l->audio, len, 1, AF_FORMAT_SI); + if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US) + si2us(l->audio,len,l->bps); + } else if((c->format & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_A_LAW) { + from_alaw(c->audio, l->audio, len, l->bps, l->format&AF_FORMAT_POINT_MASK); + if(AF_FORMAT_A_LAW == (l->format&AF_FORMAT_SPECIAL_MASK)) + to_alaw(l->audio, l->audio, len, 1, AF_FORMAT_SI); + if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US) + si2us(l->audio,len,l->bps); + } else if((c->format & AF_FORMAT_POINT_MASK) == AF_FORMAT_F) { + switch(l->format&AF_FORMAT_SPECIAL_MASK){ + case(AF_FORMAT_MU_LAW): + to_ulaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK); + break; + case(AF_FORMAT_A_LAW): + to_alaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK); + break; + default: + float2int(c->audio, l->audio, len, l->bps); + if((l->format&AF_FORMAT_SIGN_MASK) == AF_FORMAT_US) + si2us(l->audio,len,l->bps); + break; + } + } else { + // Input must be int + + // Change signed/unsigned + if((c->format&AF_FORMAT_SIGN_MASK) != (l->format&AF_FORMAT_SIGN_MASK)){ + si2us(c->audio,len,c->bps); + } + // Convert to special formats + switch(l->format&(AF_FORMAT_SPECIAL_MASK|AF_FORMAT_POINT_MASK)){ + case(AF_FORMAT_MU_LAW): + to_ulaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK); + break; + case(AF_FORMAT_A_LAW): + to_alaw(c->audio, l->audio, len, c->bps, c->format&AF_FORMAT_POINT_MASK); + break; + case(AF_FORMAT_F): + int2float(c->audio, l->audio, len, c->bps); + break; + default: + // Change the number of bits + if(c->bps != l->bps) + change_bps(c->audio,l->audio,len,c->bps,l->bps); + else + memcpy(l->audio,c->audio,len*c->bps); + break; + } + } + + // Switch from cpu native endian to the correct endianness + if((l->format&AF_FORMAT_END_MASK)!=AF_FORMAT_NE) + endian(l->audio,l->audio,len,l->bps); + + // Set output data + c->audio = l->audio; + c->len = len*l->bps; + c->bps = l->bps; + c->format = l->format; + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + if(af->data == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_format = { + "Sample format conversion", + "format", + "Anders", + "", + AF_FLAGS_REENTRANT, + af_open +}; + +static inline uint32_t load24bit(void* data, int pos) { +#if BYTE_ORDER == BIG_ENDIAN + return (((uint32_t)((uint8_t*)data)[3*pos])<<24) | + (((uint32_t)((uint8_t*)data)[3*pos+1])<<16) | + (((uint32_t)((uint8_t*)data)[3*pos+2])<<8); +#else + return (((uint32_t)((uint8_t*)data)[3*pos])<<8) | + (((uint32_t)((uint8_t*)data)[3*pos+1])<<16) | + (((uint32_t)((uint8_t*)data)[3*pos+2])<<24); +#endif +} + +static inline void store24bit(void* data, int pos, uint32_t expanded_value) { +#if BYTE_ORDER == BIG_ENDIAN + ((uint8_t*)data)[3*pos]=expanded_value>>24; + ((uint8_t*)data)[3*pos+1]=expanded_value>>16; + ((uint8_t*)data)[3*pos+2]=expanded_value>>8; +#else + ((uint8_t*)data)[3*pos]=expanded_value>>8; + ((uint8_t*)data)[3*pos+1]=expanded_value>>16; + ((uint8_t*)data)[3*pos+2]=expanded_value>>24; +#endif +} + +// Function implementations used by play +static void endian(void* in, void* out, int len, int bps) +{ + register int i; + switch(bps){ + case(2):{ + for(i=0;i<len;i++){ + ((uint16_t*)out)[i]=bswap_16(((uint16_t*)in)[i]); + } + break; + } + case(3):{ + register uint8_t s; + for(i=0;i<len;i++){ + s=((uint8_t*)in)[3*i]; + ((uint8_t*)out)[3*i]=((uint8_t*)in)[3*i+2]; + if (in != out) + ((uint8_t*)out)[3*i+1]=((uint8_t*)in)[3*i+1]; + ((uint8_t*)out)[3*i+2]=s; + } + break; + } + case(4):{ + for(i=0;i<len;i++){ + ((uint32_t*)out)[i]=bswap_32(((uint32_t*)in)[i]); + } + break; + } + } +} + +static void si2us(void* data, int len, int bps) +{ + register long i = -(len * bps); + register uint8_t *p = &((uint8_t *)data)[len * bps]; +#if AF_FORMAT_NE == AF_FORMAT_LE + p += bps - 1; +#endif + if (len <= 0) return; + do { + p[i] ^= 0x80; + } while (i += bps); +} + +static void change_bps(void* in, void* out, int len, int inbps, int outbps) +{ + register int i; + switch(inbps){ + case(1): + switch(outbps){ + case(2): + for(i=0;i<len;i++) + ((uint16_t*)out)[i]=((uint16_t)((uint8_t*)in)[i])<<8; + break; + case(3): + for(i=0;i<len;i++) + store24bit(out, i, ((uint32_t)((uint8_t*)in)[i])<<24); + break; + case(4): + for(i=0;i<len;i++) + ((uint32_t*)out)[i]=((uint32_t)((uint8_t*)in)[i])<<24; + break; + } + break; + case(2): + switch(outbps){ + case(1): + for(i=0;i<len;i++) + ((uint8_t*)out)[i]=(uint8_t)((((uint16_t*)in)[i])>>8); + break; + case(3): + for(i=0;i<len;i++) + store24bit(out, i, ((uint32_t)((uint16_t*)in)[i])<<16); + break; + case(4): + for(i=0;i<len;i++) + ((uint32_t*)out)[i]=((uint32_t)((uint16_t*)in)[i])<<16; + break; + } + break; + case(3): + switch(outbps){ + case(1): + for(i=0;i<len;i++) + ((uint8_t*)out)[i]=(uint8_t)(load24bit(in, i)>>24); + break; + case(2): + for(i=0;i<len;i++) + ((uint16_t*)out)[i]=(uint16_t)(load24bit(in, i)>>16); + break; + case(4): + for(i=0;i<len;i++) + ((uint32_t*)out)[i]=(uint32_t)load24bit(in, i); + break; + } + break; + case(4): + switch(outbps){ + case(1): + for(i=0;i<len;i++) + ((uint8_t*)out)[i]=(uint8_t)((((uint32_t*)in)[i])>>24); + break; + case(2): + for(i=0;i<len;i++) + ((uint16_t*)out)[i]=(uint16_t)((((uint32_t*)in)[i])>>16); + break; + case(3): + for(i=0;i<len;i++) + store24bit(out, i, ((uint32_t*)in)[i]); + break; + } + break; + } +} + +static void float2int(float* in, void* out, int len, int bps) +{ + register int i; + switch(bps){ + case(1): + for(i=0;i<len;i++) + ((int8_t*)out)[i] = lrintf(127.0 * clamp(in[i], -1.0f, +1.0f)); + break; + case(2): + for(i=0;i<len;i++) + ((int16_t*)out)[i] = lrintf(32767.0 * clamp(in[i], -1.0f, +1.0f)); + break; + case(3): + for(i=0;i<len;i++) + store24bit(out, i, lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f))); + break; + case(4): + for(i=0;i<len;i++) + ((int32_t*)out)[i] = lrintf(2147483647.0 * clamp(in[i], -1.0f, +1.0f)); + break; + } +} + +static void int2float(void* in, float* out, int len, int bps) +{ + register int i; + switch(bps){ + case(1): + for(i=0;i<len;i++) + out[i]=(1.0/128.0)*((int8_t*)in)[i]; + break; + case(2): + for(i=0;i<len;i++) + out[i]=(1.0/32768.0)*((int16_t*)in)[i]; + break; + case(3): + for(i=0;i<len;i++) + out[i]=(1.0/2147483648.0)*((int32_t)load24bit(in, i)); + break; + case(4): + for(i=0;i<len;i++) + out[i]=(1.0/2147483648.0)*((int32_t*)in)[i]; + break; + } +} diff --git a/audio/filter/af_format_alaw.h b/audio/filter/af_format_alaw.h new file mode 100644 index 0000000000..d7c00884f7 --- /dev/null +++ b/audio/filter/af_format_alaw.h @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au + * + * This file is based on a part of libsndfile, the work of + * Erik de Castro Lopo <erikd@zip.com.au>. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AF_FORMAT_ALAW_H +#define MPLAYER_AF_FORMAT_ALAW_H + +#include <inttypes.h> + +#include "af.h" + +// Conversion tables (the function are below) +static short alaw_decode [128] = +{ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944, + -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136, + -11008, -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848 +} ; /* alaw_decode */ + +static unsigned char alaw_encode [2049] = +{ 0xD5, 0xD4, 0xD7, 0xD6, 0xD1, 0xD0, 0xD3, 0xD2, 0xDD, 0xDC, 0xDF, 0xDE, + 0xD9, 0xD8, 0xDB, 0xDA, 0xC5, 0xC4, 0xC7, 0xC6, 0xC1, 0xC0, 0xC3, 0xC2, + 0xCD, 0xCC, 0xCF, 0xCE, 0xC9, 0xC8, 0xCB, 0xCA, 0xF5, 0xF5, 0xF4, 0xF4, + 0xF7, 0xF7, 0xF6, 0xF6, 0xF1, 0xF1, 0xF0, 0xF0, 0xF3, 0xF3, 0xF2, 0xF2, + 0xFD, 0xFD, 0xFC, 0xFC, 0xFF, 0xFF, 0xFE, 0xFE, 0xF9, 0xF9, 0xF8, 0xF8, + 0xFB, 0xFB, 0xFA, 0xFA, 0xE5, 0xE5, 0xE5, 0xE5, 0xE4, 0xE4, 0xE4, 0xE4, + 0xE7, 0xE7, 0xE7, 0xE7, 0xE6, 0xE6, 0xE6, 0xE6, 0xE1, 0xE1, 0xE1, 0xE1, + 0xE0, 0xE0, 0xE0, 0xE0, 0xE3, 0xE3, 0xE3, 0xE3, 0xE2, 0xE2, 0xE2, 0xE2, + 0xED, 0xED, 0xED, 0xED, 0xEC, 0xEC, 0xEC, 0xEC, 0xEF, 0xEF, 0xEF, 0xEF, + 0xEE, 0xEE, 0xEE, 0xEE, 0xE9, 0xE9, 0xE9, 0xE9, 0xE8, 0xE8, 0xE8, 0xE8, + 0xEB, 0xEB, 0xEB, 0xEB, 0xEA, 0xEA, 0xEA, 0xEA, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x2A +} ; /* alaw_encode */ + +/* Convert from alaw to signd int8 to signed int32 or float */ +static int from_alaw(void* in, void* out, int len, int bps, int format) +{ + register int i; + // Make sure the input parametrs are OK + if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US)) + return AF_ERROR; + + // Convert to int or to float + if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){ + switch(bps){ + case(1): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((int8_t*)out)[i] = (-1 * alaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8; + else + ((int8_t*)out)[i] = (alaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8; + } + break; + case(2): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((int16_t*)out)[i] = -1 * alaw_decode[(((int8_t*)in)[i]) & 0x7F]; + else + ((int16_t*)out)[i] = alaw_decode[(((int8_t*)in)[i]) & 0x7F]; + } + break; + case(4): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((int32_t*)out)[i] = (-1 * alaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16; + else + ((int32_t*)out)[i] = (alaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16; + } + break; + default: + return AF_ERROR; + } + } + else{ + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((float*)out)[i] = -1.0/32768.0 * (float)alaw_decode[(((int8_t*)in)[i]) & 0x7F]; + else + ((float*)out)[i] = +1.0/32768.0 * (float)alaw_decode[(((int8_t*)in)[i]) & 0x7F]; + } + } + return AF_OK; +} + +/* Convert from signed int8 to signed int32 or float to alaw */ +static int to_alaw(void* in, void* out, int len, int bps, int format) +{ + register int i; + // Make sure the input parametrs are OK + if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US)) + return AF_ERROR; + + // Convert from int or to float + if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){ + switch(bps){ + case(1): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] >= 0) + ((int8_t*)out)[i] = alaw_encode[((int8_t*)in)[i] << 4]; + else + ((int8_t*)out)[i] = 0x7F & alaw_encode[-((int8_t*)in)[i] << 4]; + } + break; + case(2): + for(i=0;i<len;i++){ + if(((int16_t*)in)[i] >= 0) + ((int8_t*)out)[i] = alaw_encode[((int16_t*)in)[i] / 16]; + else + ((int8_t*)out)[i] = 0x7F & alaw_encode[((int16_t*)in)[i] / -16]; + } + break; + case(4): + for(i=0;i<len;i++){ + if(((int32_t*)in)[i] >= 0) + ((int8_t*)out)[i] = alaw_encode[((int32_t*)in)[i] >> (16 + 4)]; + else + ((int8_t*)out)[i] = 0x7F & alaw_encode[-((int32_t*)in)[i] >> (16 + 4)]; + } + break; + default: + return AF_ERROR; + } + } + else{ + for(i=0;i<len;i++){ + if(((float*)in)[i] >= 0) + ((int8_t*)out)[i] = alaw_encode[(int)(32767.0/16.0 * ((float*)in)[i])]; + else + ((int8_t*)out)[i] = 0x7F & alaw_encode[(int)(-32767.0/16.0 * ((float*)in)[i])]; + } + } + return AF_OK; +} +#endif /* MPLAYER_AF_FORMAT_ALAW_H */ diff --git a/audio/filter/af_format_ulaw.h b/audio/filter/af_format_ulaw.h new file mode 100644 index 0000000000..5167a1593a --- /dev/null +++ b/audio/filter/af_format_ulaw.h @@ -0,0 +1,837 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au + * + * This file is based on a part of libsndfile, the work of + * Erik de Castro Lopo <erikd@zip.com.au>. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AF_FORMAT_ULAW_H +#define MPLAYER_AF_FORMAT_ULAW_H + +#include <inttypes.h> + +#include "af.h" +// Conversion tables (the function are below) +static short ulaw_decode[128] = +{ -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956, + -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764, + -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412, + -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, +} ; + +static unsigned char ulaw_encode[8193] = +{ 0xFF, 0xFE, 0xFE, 0xFD, 0xFD, 0xFC, 0xFC, 0xFB, 0xFB, 0xFA, 0xFA, 0xF9, + 0xF9, 0xF8, 0xF8, 0xF7, 0xF7, 0xF6, 0xF6, 0xF5, 0xF5, 0xF4, 0xF4, 0xF3, + 0xF3, 0xF2, 0xF2, 0xF1, 0xF1, 0xF0, 0xF0, 0xEF, 0xEF, 0xEF, 0xEF, 0xEE, + 0xEE, 0xEE, 0xEE, 0xED, 0xED, 0xED, 0xED, 0xEC, 0xEC, 0xEC, 0xEC, 0xEB, + 0xEB, 0xEB, 0xEB, 0xEA, 0xEA, 0xEA, 0xEA, 0xE9, 0xE9, 0xE9, 0xE9, 0xE8, + 0xE8, 0xE8, 0xE8, 0xE7, 0xE7, 0xE7, 0xE7, 0xE6, 0xE6, 0xE6, 0xE6, 0xE5, + 0xE5, 0xE5, 0xE5, 0xE4, 0xE4, 0xE4, 0xE4, 0xE3, 0xE3, 0xE3, 0xE3, 0xE2, + 0xE2, 0xE2, 0xE2, 0xE1, 0xE1, 0xE1, 0xE1, 0xE0, 0xE0, 0xE0, 0xE0, 0xDF, + 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, + 0xDE, 0xDE, 0xDE, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDC, + 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, + 0xDB, 0xDB, 0xDB, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xD9, + 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0xD8, 0xD8, 0xD8, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD6, + 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, + 0xD5, 0xD5, 0xD5, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD3, + 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, + 0xD2, 0xD2, 0xD2, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD0, + 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, + 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCE, + 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, + 0xCE, 0xCE, 0xCE, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, + 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCB, + 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, + 0xCB, 0xCB, 0xCB, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, + 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, + 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC8, + 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, + 0xC8, 0xC8, 0xC8, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, + 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC5, + 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, + 0xC5, 0xC5, 0xC5, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, + 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, + 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, + 0xC2, 0xC2, 0xC2, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, + 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, + 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, + 0xBE, 0xBE, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, + 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, + 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, + 0xBB, 0xBB, 0xBB, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, + 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, + 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, + 0xB8, 0xB8, 0xB8, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, + 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, + 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, + 0xB5, 0xB5, 0xB5, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, + 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, + 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, + 0xB2, 0xB2, 0xB2, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, + 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, + 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, + 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, + 0xAE, 0xAE, 0xAE, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, + 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, + 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, + 0xAB, 0xAB, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, + 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, + 0xA8, 0xA8, 0xA8, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, + 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, + 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, + 0xA5, 0xA5, 0xA5, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, + 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, + 0xA2, 0xA2, 0xA2, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, + 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, + 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, + 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, + 0x9E, 0x9E, 0x9E, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, + 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, + 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, + 0x9B, 0x9B, 0x9B, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, + 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, + 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, + 0x8E, 0x8E, 0x8E, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, + 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, + 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8B, + 0x8B, 0x8B, 0x8B, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, + 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00 +} ; + + +/* Convert from ulaw to signd int8 to signed int32 or float */ +static int from_ulaw(void* in, void* out, int len, int bps, int format) +{ + register int i; + // Make sure the input parametrs are OK + if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US)) + return AF_ERROR; + + // Convert to int or to float + if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){ + switch(bps){ + case(1): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((int8_t*)out)[i] = (-1 * ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8; + else + ((int8_t*)out)[i] = (ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) >> 8; + } + break; + case(2): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((int16_t*)out)[i] = -1 * ulaw_decode[(((int8_t*)in)[i]) & 0x7F]; + else + ((int16_t*)out)[i] = ulaw_decode[(((int8_t*)in)[i]) & 0x7F]; + } + break; + case(4): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((int32_t*)out)[i] = (-1 * ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16; + else + ((int32_t*)out)[i] = (ulaw_decode[(((int8_t*)in)[i]) & 0x7F]) << 16; + } + break; + default: + return AF_ERROR; + } + } + else{ + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] & 0x80) + ((float*)out)[i] = -1.0/32768.0 * (float)ulaw_decode[(((int8_t*)in)[i]) & 0x7F]; + else + ((float*)out)[i] = +1.0/32768.0 * (float)ulaw_decode[(((int8_t*)in)[i]) & 0x7F]; + } + } + return AF_OK; +} + +/* Convert from signed int8 to signed int32 or float to ulaw */ +static int to_ulaw(void* in, void* out, int len, int bps, int format) +{ + register int i; + // Make sure the input parametrs are OK + if(format & (AF_FORMAT_SPECIAL_MASK | AF_FORMAT_US)) + return AF_ERROR; + + // Convert from int or to float + if((format & AF_FORMAT_POINT_MASK) == AF_FORMAT_I){ + switch(bps){ + case(1): + for(i=0;i<len;i++){ + if(((int8_t*)in)[i] >= 0) + ((int8_t*)out)[i] = ulaw_encode[((int8_t*)in)[i] << 6]; + else + ((int8_t*)out)[i] = 0x7F & ulaw_encode[-((int8_t*)in)[i] << 6]; + } + break; + case(2): + for(i=0;i<len;i++){ + if(((int16_t*)in)[i] >= 0) + ((int8_t*)out)[i] = ulaw_encode[((int16_t*)in)[i] / 4]; + else + ((int8_t*)out)[i] = 0x7F & ulaw_encode[((int16_t*)in)[i] / -4]; + } + break; + case(4): + for(i=0;i<len;i++){ + if(((int32_t*)in)[i] >= 0) + ((int8_t*)out)[i] = ulaw_encode[((int32_t*)in)[i] >> (16 + 2)]; + else + ((int8_t*)out)[i] = 0x7F & ulaw_encode[-((int32_t*)in)[i] >> (16 + 2)]; + } + break; + default: + return AF_ERROR; + } + } + else{ + for(i=0;i<len;i++){ + if(((float*)in)[i] >= 0) + ((int8_t*)out)[i] = ulaw_encode[(int)(32767.0/4.0 * ((float*)in)[i])]; + else + ((int8_t*)out)[i] = 0x7F & ulaw_encode[(int)(-32767.0/4.0 * ((float*)in)[i])]; + } + } + return AF_OK; +} + +#endif /* MPLAYER_AF_FORMAT_ULAW_H */ diff --git a/audio/filter/af_hrtf.c b/audio/filter/af_hrtf.c new file mode 100644 index 0000000000..4f5eedb29d --- /dev/null +++ b/audio/filter/af_hrtf.c @@ -0,0 +1,670 @@ +/* + * Experimental audio filter that mixes 5.1 and 5.1 with matrix + * encoded rear channels into headphone signal using FIR filtering + * with HRTF. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +//#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include <math.h> +#include <libavutil/common.h> + +#include "af.h" +#include "dsp.h" + +/* HRTF filter coefficients and adjustable parameters */ +#include "af_hrtf.h" + +typedef struct af_hrtf_s { + /* Lengths */ + int dlbuflen, hrflen, basslen; + /* L, C, R, Ls, Rs channels */ + float *lf, *rf, *lr, *rr, *cf, *cr; + const float *cf_ir, *af_ir, *of_ir, *ar_ir, *or_ir, *cr_ir; + int cf_o, af_o, of_o, ar_o, or_o, cr_o; + /* Bass */ + float *ba_l, *ba_r; + float *ba_ir; + /* Whether to matrix decode the rear center channel */ + int matrix_mode; + /* How to decode the input: + 0 = 5/5+1 channels + 1 = 2 channels + 2 = matrix encoded 2 channels */ + int decode_mode; + /* Full wave rectified (FWR) amplitudes and gain used to steer the + active matrix decoding of front channels (variable names + lpr/lmr means Lt + Rt, Lt - Rt) */ + float l_fwr, r_fwr, lpr_fwr, lmr_fwr; + float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain; + /* Matrix input decoding require special FWR buffer, since the + decoding is done in place. */ + float *fwrbuf_l, *fwrbuf_r, *fwrbuf_lr, *fwrbuf_rr; + /* Rear channel delay buffer for matrix decoding */ + float *rear_dlbuf; + /* Full wave rectified amplitude and gain used to steer the active + matrix decoding of center rear channel */ + float lr_fwr, rr_fwr, lrprr_fwr, lrmrr_fwr; + float adapt_lr_gain, adapt_rr_gain; + float adapt_lrprr_gain, adapt_lrmrr_gain; + /* Cyclic position on the ring buffer */ + int cyc_pos; + int print_flag; +} af_hrtf_t; + +/* Convolution on a ring buffer + * nx: length of the ring buffer + * nk: length of the convolution kernel + * sx: ring buffer + * sk: convolution kernel + * offset: offset on the ring buffer, can be + */ +static float conv(const int nx, const int nk, const float *sx, const float *sk, + const int offset) +{ + /* k = reminder of offset / nx */ + int k = offset >= 0 ? offset % nx : nx + (offset % nx); + + if(nk + k <= nx) + return af_filter_fir(nk, sx + k, sk); + else + return af_filter_fir(nk + k - nx, sx, sk + nx - k) + + af_filter_fir(nx - k, sx + k, sk); +} + +/* Detect when the impulse response starts (significantly) */ +static int pulse_detect(const float *sx) +{ + /* nmax must be the reference impulse response length (128) minus + s->hrflen */ + const int nmax = 128 - HRTFFILTLEN; + const float thresh = IRTHRESH; + int i; + + for(i = 0; i < nmax; i++) + if(fabs(sx[i]) > thresh) + return i; + return 0; +} + +/* Fuzzy matrix coefficient transfer function to "lock" the matrix on + a effectively passive mode if the gain is approximately 1 */ +static inline float passive_lock(float x) +{ + const float x1 = x - 1; + const float ax1s = fabs(x - 1) * (1.0 / MATAGCLOCK); + + return x1 - x1 / (1 + ax1s * ax1s) + 1; +} + +/* Unified active matrix decoder for 2 channel matrix encoded surround + sources */ +static inline void matrix_decode(short *in, const int k, const int il, + const int ir, const int decode_rear, + const int dlbuflen, + float l_fwr, float r_fwr, + float lpr_fwr, float lmr_fwr, + float *adapt_l_gain, float *adapt_r_gain, + float *adapt_lpr_gain, float *adapt_lmr_gain, + float *lf, float *rf, float *lr, + float *rr, float *cf) +{ + const int kr = (k + MATREARDELAY) % dlbuflen; + float l_gain = (l_fwr + r_fwr) / + (1 + l_fwr + l_fwr); + float r_gain = (l_fwr + r_fwr) / + (1 + r_fwr + r_fwr); + /* The 2nd axis has strong gain fluctuations, and therefore require + limits. The factor corresponds to the 1 / amplification of (Lt + - Rt) when (Lt, Rt) is strongly correlated. (e.g. during + dialogues). It should be bigger than -12 dB to prevent + distortion. */ + float lmr_lim_fwr = lmr_fwr > M9_03DB * lpr_fwr ? + lmr_fwr : M9_03DB * lpr_fwr; + float lpr_gain = (lpr_fwr + lmr_lim_fwr) / + (1 + lpr_fwr + lpr_fwr); + float lmr_gain = (lpr_fwr + lmr_lim_fwr) / + (1 + lmr_lim_fwr + lmr_lim_fwr); + float lmr_unlim_gain = (lpr_fwr + lmr_fwr) / + (1 + lmr_fwr + lmr_fwr); + float lpr, lmr; + float l_agc, r_agc, lpr_agc, lmr_agc; + float f, d_gain, c_gain, c_agc_cfk; + +#if 0 + static int counter = 0; + static FILE *fp_out; + + if(counter == 0) + fp_out = fopen("af_hrtf.log", "w"); + if(counter % 240 == 0) + fprintf(fp_out, "%g %g %g %g %g ", counter * (1.0 / 48000), + l_gain, r_gain, lpr_gain, lmr_gain); +#endif + + /*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/ + /* AGC adaption */ + d_gain = (fabs(l_gain - *adapt_l_gain) + + fabs(r_gain - *adapt_r_gain)) * 0.5; + f = d_gain * (1.0 / MATAGCTRIG); + f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); + *adapt_l_gain = (1 - f) * *adapt_l_gain + f * l_gain; + *adapt_r_gain = (1 - f) * *adapt_r_gain + f * r_gain; + /* Matrix */ + l_agc = in[il] * passive_lock(*adapt_l_gain); + r_agc = in[ir] * passive_lock(*adapt_r_gain); + cf[k] = (l_agc + r_agc) * M_SQRT1_2; + if(decode_rear) { + lr[kr] = rr[kr] = (l_agc - r_agc) * M_SQRT1_2; + /* Stereo rear channel is steered with the same AGC steering as + the decoding matrix. Note this requires a fast updating AGC + at the order of 20 ms (which is the case here). */ + lr[kr] *= (l_fwr + l_fwr) / + (1 + l_fwr + r_fwr); + rr[kr] *= (r_fwr + r_fwr) / + (1 + l_fwr + r_fwr); + } + + /*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/ + lpr = (in[il] + in[ir]) * M_SQRT1_2; + lmr = (in[il] - in[ir]) * M_SQRT1_2; + /* AGC adaption */ + d_gain = fabs(lmr_unlim_gain - *adapt_lmr_gain); + f = d_gain * (1.0 / MATAGCTRIG); + f = MATAGCDECAY - MATAGCDECAY / (1 + f * f); + *adapt_lpr_gain = (1 - f) * *adapt_lpr_gain + f * lpr_gain; + *adapt_lmr_gain = (1 - f) * *adapt_lmr_gain + f * lmr_gain; + /* Matrix */ + lpr_agc = lpr * passive_lock(*adapt_lpr_gain); + lmr_agc = lmr * passive_lock(*adapt_lmr_gain); + lf[k] = (lpr_agc + lmr_agc) * M_SQRT1_2; + rf[k] = (lpr_agc - lmr_agc) * M_SQRT1_2; + + /*** CENTER FRONT CANCELLATION ***/ + /* A heuristic approach exploits that Lt + Rt gain contains the + information about Lt, Rt correlation. This effectively reshapes + the front and rear "cones" to concentrate Lt + Rt to C and + introduce Lt - Rt in L, R. */ + /* 0.67677 is the emprical lower bound for lpr_gain. */ + c_gain = 8 * (*adapt_lpr_gain - 0.67677); + c_gain = c_gain > 0 ? c_gain : 0; + /* c_gain should not be too high, not even reaching full + cancellation (~ 0.50 - 0.55 at current AGC implementation), or + the center will s0und too narrow. */ + c_gain = MATCOMPGAIN / (1 + c_gain * c_gain); + c_agc_cfk = c_gain * cf[k]; + lf[k] -= c_agc_cfk; + rf[k] -= c_agc_cfk; + cf[k] += c_agc_cfk + c_agc_cfk; +#if 0 + if(counter % 240 == 0) + fprintf(fp_out, "%g %g %g %g %g\n", + *adapt_l_gain, *adapt_r_gain, + *adapt_lpr_gain, *adapt_lmr_gain, + c_gain); + counter++; +#endif +} + +static inline void update_ch(af_hrtf_t *s, short *in, const int k) +{ + const int fwr_pos = (k + FWRDURATION) % s->dlbuflen; + /* Update the full wave rectified total amplitude */ + /* Input matrix decoder */ + if(s->decode_mode == HRTF_MIX_MATRIX2CH) { + s->l_fwr += abs(in[0]) - fabs(s->fwrbuf_l[fwr_pos]); + s->r_fwr += abs(in[1]) - fabs(s->fwrbuf_r[fwr_pos]); + s->lpr_fwr += abs(in[0] + in[1]) - + fabs(s->fwrbuf_l[fwr_pos] + s->fwrbuf_r[fwr_pos]); + s->lmr_fwr += abs(in[0] - in[1]) - + fabs(s->fwrbuf_l[fwr_pos] - s->fwrbuf_r[fwr_pos]); + } + /* Rear matrix decoder */ + if(s->matrix_mode) { + s->lr_fwr += abs(in[2]) - fabs(s->fwrbuf_lr[fwr_pos]); + s->rr_fwr += abs(in[3]) - fabs(s->fwrbuf_rr[fwr_pos]); + s->lrprr_fwr += abs(in[2] + in[3]) - + fabs(s->fwrbuf_lr[fwr_pos] + s->fwrbuf_rr[fwr_pos]); + s->lrmrr_fwr += abs(in[2] - in[3]) - + fabs(s->fwrbuf_lr[fwr_pos] - s->fwrbuf_rr[fwr_pos]); + } + + switch (s->decode_mode) { + case HRTF_MIX_51: + /* 5/5+1 channel sources */ + s->lf[k] = in[0]; + s->cf[k] = in[4]; + s->rf[k] = in[1]; + s->fwrbuf_lr[k] = s->lr[k] = in[2]; + s->fwrbuf_rr[k] = s->rr[k] = in[3]; + break; + case HRTF_MIX_MATRIX2CH: + /* Matrix encoded 2 channel sources */ + s->fwrbuf_l[k] = in[0]; + s->fwrbuf_r[k] = in[1]; + matrix_decode(in, k, 0, 1, 1, s->dlbuflen, + s->l_fwr, s->r_fwr, + s->lpr_fwr, s->lmr_fwr, + &(s->adapt_l_gain), &(s->adapt_r_gain), + &(s->adapt_lpr_gain), &(s->adapt_lmr_gain), + s->lf, s->rf, s->lr, s->rr, s->cf); + break; + case HRTF_MIX_STEREO: + /* Stereo sources */ + s->lf[k] = in[0]; + s->rf[k] = in[1]; + s->cf[k] = s->lr[k] = s->rr[k] = 0; + break; + } + + /* We need to update the bass compensation delay line, too. */ + s->ba_l[k] = in[0] + in[4] + in[2]; + s->ba_r[k] = in[4] + in[1] + in[3]; +} + +/* Initialization and runtime control */ +static int control(struct af_instance *af, int cmd, void* arg) +{ + af_hrtf_t *s = af->setup; + int test_output_res; + char mode; + + switch(cmd) { + case AF_CONTROL_REINIT: + af->data->rate = ((struct mp_audio*)arg)->rate; + if(af->data->rate != 48000) { + // automatic samplerate adjustment in the filter chain + // is not yet supported. + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[hrtf] ERROR: Sampling rate is not 48000 Hz (%d)!\n", + af->data->rate); + return AF_ERROR; + } + af->data->nch = ((struct mp_audio*)arg)->nch; + if(af->data->nch == 2) { + /* 2 channel input */ + if(s->decode_mode != HRTF_MIX_MATRIX2CH) { + /* Default behavior is stereo mixing. */ + s->decode_mode = HRTF_MIX_STEREO; + } + } + else if (af->data->nch < 5) + af->data->nch = 5; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + test_output_res = af_test_output(af, (struct mp_audio*)arg); + af->mul = 2.0 / af->data->nch; + // after testing input set the real output format + af->data->nch = 2; + s->print_flag = 1; + return test_output_res; + case AF_CONTROL_COMMAND_LINE: + sscanf((char*)arg, "%c", &mode); + switch(mode) { + case 'm': + /* Use matrix rear decoding. */ + s->matrix_mode = 1; + break; + case 's': + /* Input needs matrix decoding. */ + s->decode_mode = HRTF_MIX_MATRIX2CH; + break; + case '0': + s->matrix_mode = 0; + break; + default: + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[hrtf] Mode is neither 'm', 's', nor '0' (%c).\n", + mode); + return AF_ERROR; + } + s->print_flag = 1; + return AF_OK; + } + + return AF_UNKNOWN; +} + +/* Deallocate memory */ +static void uninit(struct af_instance *af) +{ + if(af->setup) { + af_hrtf_t *s = af->setup; + + free(s->lf); + free(s->rf); + free(s->lr); + free(s->rr); + free(s->cf); + free(s->cr); + free(s->ba_l); + free(s->ba_r); + free(s->ba_ir); + free(s->fwrbuf_l); + free(s->fwrbuf_r); + free(s->fwrbuf_lr); + free(s->fwrbuf_rr); + free(af->setup); + } + if(af->data) + free(af->data->audio); + free(af->data); +} + +/* Filter data through filter + +Two "tricks" are used to compensate the "color" of the KEMAR data: + +1. The KEMAR data is refiltered to ensure that the front L, R channels +on the same side of the ear are equalized (especially in the high +frequencies). + +2. A bass compensation is introduced to ensure that 0-200 Hz are not +damped (without any real 3D acoustical image, however). +*/ +static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) +{ + af_hrtf_t *s = af->setup; + short *in = data->audio; // Input audio data + short *out = NULL; // Output audio data + short *end = in + data->len / sizeof(short); // Loop end + float common, left, right, diff, left_b, right_b; + const int dblen = s->dlbuflen, hlen = s->hrflen, blen = s->basslen; + + if(AF_OK != RESIZE_LOCAL_BUFFER(af, data)) + return NULL; + + if(s->print_flag) { + s->print_flag = 0; + switch (s->decode_mode) { + case HRTF_MIX_51: + mp_msg(MSGT_AFILTER, MSGL_INFO, + "[hrtf] Using HRTF to mix %s discrete surround into " + "L, R channels\n", s->matrix_mode ? "5+1" : "5"); + break; + case HRTF_MIX_STEREO: + mp_msg(MSGT_AFILTER, MSGL_INFO, + "[hrtf] Using HRTF to mix stereo into " + "L, R channels\n"); + break; + case HRTF_MIX_MATRIX2CH: + mp_msg(MSGT_AFILTER, MSGL_INFO, + "[hrtf] Using active matrix to decode 2 channel " + "input, HRTF to mix %s matrix surround into " + "L, R channels\n", "3/2"); + break; + default: + mp_msg(MSGT_AFILTER, MSGL_WARN, + "[hrtf] bogus decode_mode: %d\n", s->decode_mode); + break; + } + + if(s->matrix_mode) + mp_msg(MSGT_AFILTER, MSGL_INFO, + "[hrtf] Using active matrix to decode rear center " + "channel\n"); + } + + out = af->data->audio; + + /* MPlayer's 5 channel layout (notation for the variable): + * + * 0: L (LF), 1: R (RF), 2: Ls (LR), 3: Rs (RR), 4: C (CF), matrix + * encoded: Cs (CR) + * + * or: L = left, C = center, R = right, F = front, R = rear + * + * Filter notation: + * + * CF + * OF AF + * Ear-> + * OR AR + * CR + * + * or: C = center, A = same side, O = opposite, F = front, R = rear + */ + + while(in < end) { + const int k = s->cyc_pos; + + update_ch(s, in, k); + + /* Simulate a 7.5 ms -20 dB echo of the center channel in the + front channels (like reflection from a room wall) - a kind of + psycho-acoustically "cheating" to focus the center front + channel, which is normally hard to be perceived as front */ + s->lf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen]; + s->rf[k] += CFECHOAMPL * s->cf[(k + CFECHODELAY) % s->dlbuflen]; + + switch (s->decode_mode) { + case HRTF_MIX_51: + case HRTF_MIX_MATRIX2CH: + /* Mixer filter matrix */ + common = conv(dblen, hlen, s->cf, s->cf_ir, k + s->cf_o); + if(s->matrix_mode) { + /* In matrix decoding mode, the rear channel gain must be + renormalized, as there is an additional channel. */ + matrix_decode(in, k, 2, 3, 0, s->dlbuflen, + s->lr_fwr, s->rr_fwr, + s->lrprr_fwr, s->lrmrr_fwr, + &(s->adapt_lr_gain), &(s->adapt_rr_gain), + &(s->adapt_lrprr_gain), &(s->adapt_lrmrr_gain), + s->lr, s->rr, NULL, NULL, s->cr); + common += + conv(dblen, hlen, s->cr, s->cr_ir, k + s->cr_o) * + M1_76DB; + left = + ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) + + (conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o)) * + M1_76DB + common); + right = + ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) + + (conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o)) * + M1_76DB + common); + } else { + left = + ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o) + + conv(dblen, hlen, s->lr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->rr, s->or_ir, k + s->or_o) + + common); + right = + ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o) + + conv(dblen, hlen, s->rr, s->ar_ir, k + s->ar_o) + + conv(dblen, hlen, s->lr, s->or_ir, k + s->or_o) + + common); + } + break; + case HRTF_MIX_STEREO: + left = + ( conv(dblen, hlen, s->lf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->rf, s->of_ir, k + s->of_o)); + right = + ( conv(dblen, hlen, s->rf, s->af_ir, k + s->af_o) + + conv(dblen, hlen, s->lf, s->of_ir, k + s->of_o)); + break; + default: + /* make gcc happy */ + left = 0.0; + right = 0.0; + break; + } + + /* Bass compensation for the lower frequency cut of the HRTF. A + cross talk of the left and right channel is introduced to + match the directional characteristics of higher frequencies. + The bass will not have any real 3D perception, but that is + OK (note at 180 Hz, the wavelength is about 2 m, and any + spatial perception is impossible). */ + left_b = conv(dblen, blen, s->ba_l, s->ba_ir, k); + right_b = conv(dblen, blen, s->ba_r, s->ba_ir, k); + left += (1 - BASSCROSS) * left_b + BASSCROSS * right_b; + right += (1 - BASSCROSS) * right_b + BASSCROSS * left_b; + /* Also mix the LFE channel (if available) */ + if(data->nch >= 6) { + left += in[5] * M3_01DB; + right += in[5] * M3_01DB; + } + + /* Amplitude renormalization. */ + left *= AMPLNORM; + right *= AMPLNORM; + + switch (s->decode_mode) { + case HRTF_MIX_51: + case HRTF_MIX_STEREO: + /* "Cheating": linear stereo expansion to amplify the 3D + perception. Note: Too much will destroy the acoustic space + and may even result in headaches. */ + diff = STEXPAND2 * (left - right); + out[0] = av_clip_int16(left + diff); + out[1] = av_clip_int16(right - diff); + break; + case HRTF_MIX_MATRIX2CH: + /* Do attempt any stereo expansion with matrix encoded + sources. The L, R channels are already stereo expanded + by the steering, any further stereo expansion will sound + very unnatural. */ + out[0] = av_clip_int16(left); + out[1] = av_clip_int16(right); + break; + } + + /* Next sample... */ + in = &in[data->nch]; + out = &out[af->data->nch]; + (s->cyc_pos)--; + if(s->cyc_pos < 0) + s->cyc_pos += dblen; + } + + /* Set output data */ + data->audio = af->data->audio; + data->len = data->len / data->nch * 2; + data->nch = 2; + + return data; +} + +static int allocate(af_hrtf_t *s) +{ + if ((s->lf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->rf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->lr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->rr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->cf = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->cr = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->ba_l = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->ba_r = malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_l = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_r = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_lr = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + if ((s->fwrbuf_rr = + malloc(s->dlbuflen * sizeof(float))) == NULL) return -1; + return 0; +} + +/* Allocate memory and set function pointers */ +static int af_open(struct af_instance* af) +{ + int i; + af_hrtf_t *s; + float fc; + + af->control = control; + af->uninit = uninit; + af->play = play; + af->mul = 1; + af->data = calloc(1, sizeof(struct mp_audio)); + af->setup = calloc(1, sizeof(af_hrtf_t)); + if((af->data == NULL) || (af->setup == NULL)) + return AF_ERROR; + + s = af->setup; + + s->dlbuflen = DELAYBUFLEN; + s->hrflen = HRTFFILTLEN; + s->basslen = BASSFILTLEN; + + s->cyc_pos = s->dlbuflen - 1; + /* With a full (two axis) steering matrix decoder, s->matrix_mode + should not be enabled lightly (it will also steer the Ls, Rs + channels). */ + s->matrix_mode = 0; + s->decode_mode = HRTF_MIX_51; + + s->print_flag = 1; + + if (allocate(s) != 0) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n"); + return AF_ERROR; + } + + for(i = 0; i < s->dlbuflen; i++) + s->lf[i] = s->rf[i] = s->lr[i] = s->rr[i] = s->cf[i] = + s->cr[i] = 0; + + s->lr_fwr = + s->rr_fwr = 0; + + s->cf_ir = cf_filt + (s->cf_o = pulse_detect(cf_filt)); + s->af_ir = af_filt + (s->af_o = pulse_detect(af_filt)); + s->of_ir = of_filt + (s->of_o = pulse_detect(of_filt)); + s->ar_ir = ar_filt + (s->ar_o = pulse_detect(ar_filt)); + s->or_ir = or_filt + (s->or_o = pulse_detect(or_filt)); + s->cr_ir = cr_filt + (s->cr_o = pulse_detect(cr_filt)); + + if((s->ba_ir = malloc(s->basslen * sizeof(float))) == NULL) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Memory allocation error.\n"); + return AF_ERROR; + } + fc = 2.0 * BASSFILTFREQ / (float)af->data->rate; + if(af_filter_design_fir(s->basslen, s->ba_ir, &fc, LP | KAISER, 4 * M_PI) == + -1) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[hrtf] Unable to design low-pass " + "filter.\n"); + return AF_ERROR; + } + for(i = 0; i < s->basslen; i++) + s->ba_ir[i] *= BASSGAIN; + + return AF_OK; +} + +/* Description of this filter */ +struct af_info af_info_hrtf = { + "HRTF Headphone", + "hrtf", + "ylai", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_hrtf.h b/audio/filter/af_hrtf.h new file mode 100644 index 0000000000..887310b57d --- /dev/null +++ b/audio/filter/af_hrtf.h @@ -0,0 +1,511 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AF_HRTF_H +#define MPLAYER_AF_HRTF_H + +#define HRTF_MIX_51 0 +#define HRTF_MIX_STEREO 1 +#define HRTF_MIX_MATRIX2CH 2 + +/* Amplitude scaling factors */ +#define M17_0DB 0.1414213562 +#define M9_03DB 0.3535533906 +#define M6_99DB 0.4472135955 +#define M4_77DB 0.5773502692 +#define M3_01DB 0.7071067812 +#define M1_76DB 0.8164965809 + +#define DELAYBUFLEN 1024 /* Length of the delay buffer */ +#define HRTFFILTLEN 64 /* HRTF filter length */ +#define IRTHRESH 0.001 /* Impulse response pruning thresh. */ + +#define AMPLNORM M6_99DB /* Overall amplitude renormalization */ + +#define BASSFILTFREQ 180 /* Bass compensation filter cut (Hz) */ +#define BASSFILTLEN 193 /* Bass compensation filter length */ +#define BASSGAIN M_SQRT2 /* Bass compensation gain */ +#define BASSCROSS 0.35 /* Bass cross talk */ + +#define FWRDURATION 240 /* FWR average duration (samples) */ +#define MATREARDELAY 720 /* Matrix mode rear delay (samples) */ + +#define MATAGCTRIG 8.0 /* (Fuzzy) AGC trigger */ +#define MATAGCDECAY 1.0 /* AGC baseline decay rate (1/samp.) */ +#define MATAGCLOCK 0.2 /* AGC range (around 1) where the + matrix behaves passively */ +#define MATCOMPGAIN 0.37 /* Cross talk compensation gain, + 0.50 - 0.55 is full cancellation. */ + +#define CFECHODELAY 360 /* Center front echo delay (samples) */ +#define CFECHOAMPL M17_0DB /* Center front echo amplitude */ + +#define STEXPAND2 0.07 /* Stereo expansion / 2 */ + +/* Head related impulse response (HRIR) derived from KEMAR measurement + data by Bill Gardner <billg@media.mit.edu> and Keith Martin + <kdm@media.mit.edu> + + URL: http://sound.media.mit.edu/KEMAR.html + + Distributed under GPL with authors' permission +*/ + +/* EQUALIZED KEMAR HRIR + +How to generate these data: + +1. You need the MIT Media Lab's KEMAR data, read it into an software + capable of signal/time series analysis (like Mathematica, Matlab, + ...) + +2. Construct an equalizing FIR inverse filter by calculating the + transfer function of the front, same side impulse response, then + take 1 over the absolute magnitude. + +3. Cut the poles in the inverse filter's transfer function + specification by limiting the amplification to 2.5 (note, this + number assumes that you have correct signal processing + normalization of the Fourier transform). + +4. Design the FIR inverse filter by calculating the inverse Fourier + transform, then chopping the coefficients down to a reasonable + number (N = 15 is used here), apply the Kaiser-Bessel window (alpha + = 2 is used here). Note the objective is remove the color bias + only (as if you are using an equalizer), _not_ to do a full inverse + filtering. (Note: beta = pi*alpha in other notation.) + + For N = 15, alpha = 2, you should get the following impulse + response: + + 0.001001558668605168, 0.00698457265741865, 0.040453643039829436, + 0.012230541722147855, -0.11939760844854072, 0.16468099899755967, + -0.30297563073747436, 1.3140211791355982, -0.30297563073747436, + 0.16468099899755967, -0.11939760844854072, 0.012230541722147855, + 0.040453643039829436, 0.00698457265741865, 0.001001558668605168 + +5. Linearly convolve all KEMAR HRIR with this inverse filter. + +6. Resample from 44.1 kHz sampling frequency to 48 kHz. +*/ + +/* Center front (-5 degree) - not 0 degree in order to create a clear + front image from a finite distance */ +static const float cf_filt[128] = { + -0.00008638082319075036, 0.0003198059946385229, + -0.0005010631339162132, 0.0011424741331126876, + -0.001584220794688753, 0.001742715363246275, + -0.0011080796626780694, 0.0001651829990860167, + 0.005235028337314985, 0.0035223828473357776, + 0.010057681388431303, -0.033469432129545514, + 0.013391253316233523, 0.004858462839827063, + 0.08172161220103627, 0.26158596134500023, + -0.12420314583323326, -0.14298458356097565, + 0.14421897280453896, -0.1635792507629016, + -0.02187136722480014, 0.2426863044711817, + 0.07229814207917194, 0.0942742651913879, + 0.29856830878076834, 0.2944146162057754, + -0.12122157003421209, -0.19640092165631157, + 0.11623836502034968, -0.05794027397995521, + -0.34313138341973776, -0.19487516249168105, + 0.010118993953802401, -0.09460218797710966, + -0.16761521117359582, 0.004154461610153861, + 0.052768641758969316, -0.00041823982226147407, + 0.021634960445143514, 0.07562793486871108, + 0.08895407129506479, 0.039857755093416214, + 0.044257936180422945, 0.061557584906101664, + 0.015547268541895703, -0.023908191934932484, + 0.009498030443468223, 0.03816269003221337, + -0.009820500607303615, -0.042003975527908084, + -0.03335447117311547, -0.029294510859746596, + -0.05212623136198511, -0.073427547153205, + -0.061190797824958836, -0.04620925059966413, + -0.04204619420702159, -0.02331915902615157, + 0.00211481411477094, 0.00852563995740107, + 0.008766809731743399, 0.008666632180812078, + 0.018279202191625352, 0.02924332751289675, + 0.022293148257836494, 0.012362146008584188, + 0.008572582458807008, 0.006491370763597344, + -0.0019366944997535774, -0.006318669309634434, + -0.006457921690218669, -0.015050265524669974, + -0.02110660282616213, -0.017027809096377904, + -0.01651052305334348, -0.022770064150046673, + -0.01999875754219472, -0.012294792027337775, + -0.011506057031057188, -0.011448970577312903, + -0.004823572302580925, 0.0022451134042777883, + 0.004145473526859826, 0.005629030064546135, + 0.008588029213398785, 0.010092048834844231, + 0.007182013245552008, 0.0014600979508720656, + -0.0038314646272511756, -0.003443901997881347, + -0.0029483418254804047, -0.007609357112679647, + -0.006518368948030822, -0.004495803701497202, + -0.007109113004849672, -0.008346237278084265, + -0.005560847336252453, -0.002993453167553188, + -0.005122897816824269, -0.004389782626604215, + -0.0010912633695218108, -0.0019712029474458835, + -0.005870162265802235, -0.005626159534954128, + -0.00027254977910844407, 0.0013794425431633785, + -0.0005919083190430062, -0.0007861203545416682, + -0.0007049560240893946, -0.0032720188494468868, + -0.004460645567968504, -0.0032018528193571696, + -0.0030579229375062105, -0.003593998902656612, + -0.0038032977997776445, -0.004613776010454773, + -0.0059796549143736845, -0.00420126194319768, + -0.0012374419948287222, 0.0008572699213050608, + 0.0021490971020081094, 0.00047295283198381995, + -0.0009670277915884887, -0.001354440866080231, + -0.002962902746547851, -0.00533935813338141, + -0.005469203016468759, -0.004355784273189485 +}; +/* Front, same side (30 degree) */ +static const float af_filt[128] = { + -0.004140580614755493, 0.005790934614385445, + 0.003318916682081112, 0.014257145544366063, + 0.007328442487127339, -0.06550381777876194, + 0.03502225818161845, -0.013090579770708259, + 0.2517776798694195, 0.420770489950659, + -0.3418854608834852, -0.24371032493696737, + 0.04901356150030018, -0.1778083521632833, + 0.26448004245714163, 0.23245199964546834, + 0.033053145803936305, 0.46811222821062415, + 0.5359265986255611, -0.011912195468533787, + -0.39763432601411647, -0.034482864386898314, + 0.029445398240649626, -0.3850940407863262, + -0.3272328478175581, -0.14701421403616477, + -0.08522137400169517, -0.14936851633336035, + -0.09432605283433723, 0.0991200405937827, + 0.011075012089917331, -0.0051036489980274685, + 0.0933903289749412, 0.1344189369609565, + 0.10507466913017807, 0.04240159324684365, + 0.06559270110638656, 0.026984119875617524, + -0.03359846103238096, -0.018000197099174275, + 0.042031818548178244, 0.03849039666888434, + -0.02450829674011345, -0.03407882403088576, + -0.029230189282961977, -0.046964865291761734, + -0.09458258700116245, -0.1527349330901158, + -0.15411577687826097, -0.08761679790956928, + -0.033623549089171874, -0.007204768531481949, + 0.008273425020444852, 0.021368717994908505, + 0.04366608267875025, 0.05660907333076205, + 0.06775726495503939, 0.05621881735271431, + 0.03576231950669927, 0.02500825834889175, + 0.015423811076054601, 0.007903258334503761, + -0.0053873014137761945, -0.006987955469434698, + -0.012027972007598602, -0.025228281243816594, + -0.026225091797257318, -0.023809293997344882, + -0.03250172017712295, -0.03195696301067249, + -0.01784813952189948, -0.01663267233760342, + -0.016184530450468065, -0.011659883749357463, + -0.0035378511240219163, -0.0005485800790443406, + 0.0018432660108168625, 0.011634844139907534, + 0.018333603402051105, 0.020447379185133056, + 0.00850783664147828, 0.0004694148911037838, + -0.0017047130409786676, -0.0022409152834483997, + -0.000860472174892845, -0.004111075059198666, + -0.003527843382056666, -0.009640160874903018, + -0.01750044574231376, -0.015613389403672443, + -0.010790028120953001, -0.0095313499955768, + -0.007469721416726809, -0.0019186578145117315, + -0.00014977322572890802, -0.0029803838028179728, + -0.006520567233727221, 0.000035015132033882596, + 0.009245098100543752, 0.009896930052308656, + 0.008316744929565786, 0.004575207140193997, + -0.0000647420103997081, -0.004502916832871627, + -0.004225962213251224, -0.002886014126381486, + -0.006416834142585976, -0.007156609995423569, + -0.008840274447579472, -0.01441763751386817, + -0.015435817484659574, -0.00924487254924743, + -0.0021571721940235205, 0.0028540722992305453, + 0.00273577475088536, -0.000788412365513805, + -0.0032650029728365907, -0.003880217646231338, + -0.0035302087299613778, -0.0038531436176586246, + -0.0011921632190514074, -0.0020722967099011938, + -0.004985351145629344, -0.0042375588844648735, + -0.003030360463006021, -0.0014161075428041471, + -0.0005083025643192044, 0.00035096963769606926 +}; +/* Front, opposite (-30 degree) */ +static const float of_filt[128] = { + -0.000013472538374193126, -0.00008048061877079751, + 0.000043927265781258155, -0.000017931700794858892, + -0.000034774602476112886, -0.00009576223008735474, + 0.0001557797638630691, -0.00018742885883751094, + 0.00026512448626705716, -0.0001451040203319678, + -0.00008263233117758043, 0.0006486245853639179, + -0.0010631408451846698, 0.0026571994100746143, + 0.0014179177997092787, 0.0062326502956616256, + -0.008194149324545333, -0.006568029415878379, + 0.009538759710818582, 0.012309193558632693, + 0.12336638055838955, 0.046164307101829005, + -0.10228706407884815, 0.04047687260345798, + -0.00296595313977046, -0.07057949208414134, + 0.08172114840714612, 0.08736490764127891, + 0.05105250431333021, 0.11627179512747428, + 0.20717888490340705, 0.09375052213570291, + -0.09784374168330194, -0.010493571845901443, + 0.053131894303891716, -0.10157443971694806, + -0.16264032634244974, -0.05402369511361273, + -0.0274403608654217, -0.09860277022495063, + -0.06841875821090282, -0.004434574400066223, + -0.0005222661652743502, -0.006231881259827263, + 0.014410397820340159, 0.04885649512730243, + 0.04361962569042684, 0.03399214029009391, + 0.04961073933475931, 0.04067325604132289, + 0.007850647519227257, 0.004564440466905299, + 0.02257107958021618, 0.008183791928884486, + -0.014913479343180557, -0.018685938460856224, + -0.01745737397226911, -0.02327177054233603, + -0.03723048632685227, -0.044739390162299685, + -0.042651220125613766, -0.03730017561004743, + -0.029039465434276192, -0.01885087458914294, + -0.01207127752277769, -0.006779800724164512, + -0.001930416967444157, 0.000029454577995528385, + 0.0013822760965755472, 0.0014799128583230202, + 0.0002068200609199832, 0.0022254295286201083, + 0.005143858159434566, 0.0018580542060917013, + -0.0019426046325146259, -0.0014464042108543495, + -0.0034430083560735582, -0.009692758426099499, + -0.011840035292593485, -0.010716508855893968, + -0.012939889036853034, -0.0121846427935653, + -0.006198503315630782, -0.0023186723099380305, + -0.002679872498314837, -0.003086020446226295, + -0.0015709623347698936, -0.0008147490468332398, + -0.0012384575726770983, -0.0005212877089109362, + 0.0017707578744906142, 0.001324932723905786, + -0.0017023653780617696, -0.0045108927752919425, + -0.005422155613096912, -0.0039489323837623835, + -0.005295995750506547, -0.00629706566356189, + -0.004685732198036754, -0.0048076735568143, + -0.005978864279217503, -0.005928999306332966, + -0.004187703549017582, -0.003213999896976475, + -0.0028068699816073414, -0.0010889703907593833, + 0.0003276714243495386, -0.0013015007040186994, + -0.003208050402434782, -0.0025115319088208545, + -0.0013787553006401076, -0.0018279087370218635, + -0.0025904836507747754, -0.002071221947222004, + -0.0026424212922485594, -0.0039837031817577, + -0.0041635566057380965, -0.004355223489150822, + -0.004350395332709937, -0.0036693292471930263, + -0.003386384394185026, -0.003972568655001128, + -0.004332336840023821, -0.002648767912111827, + -0.001384410080218114, -0.0011353792711849466, + -0.0013726264946164232, -0.0020075119315034313 +}; +/* Rear, same side (135 degree) */ +static const float ar_filt[128] = { + 0.004573315040810066, 0.0013592578059426913, + 0.01553271930902704, -0.0002356117224941317, + -0.05746098219774702, 0.03430688963370445, + 0.00808371687447385, 0.21893535841158596, + 0.2984357591724814, -0.3302799746504719, + -0.3194029149806245, 0.21633225051331056, + 0.24371260938097083, -0.08843705549751085, + 0.03939684701343366, 0.45386926431114494, + 0.07599118140753386, -0.18114706160474578, + 0.285640624686038, 0.4049515236666218, + -0.05347890222071792, -0.31464359045319074, + -0.1033502246468194, -0.04553593949283157, + -0.1880747731157464, -0.13629090230626037, + -0.10435789106123239, -0.19818232801888755, + -0.16701805476330397, -0.022793111199284, + 0.058049696762683685, 0.007048321372693906, + -0.002966419183225961, 0.10140569697797763, + 0.11648999956673124, 0.05218347182779882, + 0.028427001212735392, 0.04151900310166159, + -0.0006960604221423734, -0.05898623212226975, + -0.03801934531173312, -0.029306970535287986, + -0.04549125782835908, -0.0599222718506552, + -0.058299618975430116, -0.03765579129720727, + -0.03559302657499581, -0.020647901025903054, + -0.005720957338744348, -0.0041915732688915545, + -0.0011470880098346143, 0.008737404798553, + 0.023444168098121512, 0.024204226042172663, + 0.01894897166475026, 0.020807655257479588, + 0.021570431128040954, 0.006800556178576289, + -0.009000089216921362, -0.010969824547067934, + -0.0033653428332822374, -0.012676936164668659, + -0.026739938673413587, -0.023427869194287573, + -0.023302007105117244, -0.023647155590533712, + -0.021289317515613106, -0.009120487305069884, + 0.0009251551667728967, 0.00004285344125653763, + -0.00009042365479456271, 0.00022573242339446494, + 0.00720168491586098, 0.007111875505402431, + 0.003186514817683482, 0.00810087718334745, + 0.012619557025922575, 0.007854726400013397, + -0.0024013592881066267, -0.001452457473161119, + -0.0025535188366093945, -0.012428911627809337, + -0.013729251536694145, -0.0070099675146427344, + -0.007165284278706593, -0.01639289295622301, + -0.015831795079778305, -0.007305768485523729, + -0.003608863157004021, -0.0032640528878698084, + 0.0030901263998481944, 0.00749497566124848, + 0.002515185532327241, 0.00004840875738621367, + 0.0017596043486043966, 0.0046229941553338144, + 0.0034259167322926096, 0.003707347634186093, + 0.0035584806528586328, -0.0019078936035275198, + -0.006057343815214898, -0.0069262470468817, + -0.004345020728618624, -0.004177623355574794, + -0.005373506556122508, -0.006624933928893836, + -0.008679541408588839, -0.010718719681595322, + -0.011392246979594496, -0.007893917064389902, + -0.0027572935365832536, -0.00006064707149834412, + -0.0012512537319656323, -0.0024501501002409786, + -0.0022106788572895998, -0.00234124933370301, + -0.0008953445167066823, 0.0005393670625637734, + -0.00033175600142209297, -0.004023994309351289, + -0.008655472335784443, -0.009899957354849682, + -0.008664952919996412, -0.00553483124503576, + -0.003735336089277662, -0.002754824348643885, + -0.0026884314856593368, -0.004084181815125924 +}; +/* Rear, opposite (-135 degree) */ +static const float or_filt[128] = { + 0.0001220944028243897, -0.000021785381808441314, + 5.823057988603169e-6, -0.00001217768176447613, + -0.00006123604397345513, 5.574117262531134e-6, + -0.00004935331914366778, 1.1771577934768211e-6, + -0.000059236211621095756, 9.503536190497286e-6, + -0.0001494445696103564, 0.00012248858284145305, + -0.0000963975321456313, 6.017905197665205e-6, + 0.00003353395360402643, -0.0001931511015359506, + 0.0005113536523931485, -0.0005676652619386114, + 0.0012057159755477467, 0.0009370492250339692, + 0.004596472288877981, -0.0018831773384237068, + -0.008208535225621212, 0.0038178646400751056, + 0.008726517739105965, 0.06664363898418262, + 0.06788684221502142, -0.04492315162807267, + -0.04019906311255255, 0.026203059677375153, + 0.013678129114847544, -0.014334962223993527, + 0.010141709596167392, 0.11559131576945537, + 0.1586402064538425, 0.059975334707967023, + 0.004671725963777715, 0.031498678282775874, + 0.014338626006524587, -0.014749719448472231, + -0.02055508237941379, -0.05407690143992048, + -0.07767559836886143, -0.05029091786216801, + -0.030808335706574427, -0.03401958135442541, + -0.030520368430288967, -0.014168302104259355, + 0.011907621259989802, 0.014286081013069, + 0.006377467879613449, 0.018546823568277478, + 0.028297012771618273, 0.025222339408338186, + 0.021931611353415138, 0.019708894333646355, + 0.01729258494072014, 0.017468204169564034, + 0.009729094845051928, -0.002976992018531901, + -0.00956986166277019, -0.016125733548332074, + -0.02934094241442545, -0.04133767871051455, + -0.043536981145416466, -0.0385966307108608, + -0.02784453599342459, -0.018995135307247116, + -0.012849534096536747, -0.004437491064613308, + 0.00028385411598204655, 0.003776874988516643, + 0.008069432041547833, 0.008764754183751848, + 0.008020908861878062, 0.006830351461360802, + 0.002218330884267235, -0.0020478725582339444, + -0.003997428121462543, -0.007066287373515421, + -0.00940847412544698, -0.010938998446237963, + -0.011775483016151306, -0.011391103919484287, + -0.010586061195163017, -0.009842793078929053, + -0.007753202010139829, -0.00569213732353025, + -0.006506783349722073, -0.005346134281903736, + -0.003913089814898934, -0.0036091443854759727, + -0.0020328564301266287, 0.00017932870773467683, + 0.0032779786679056357, 0.003969695813293966, + 0.0020339334412434987, -0.00011345940675415259, + -0.0018344103399567666, -0.003556764701666365, + -0.004263523639408391, -0.002940568582022133, + -0.0034341188272627556, -0.006023399920020824, + -0.0077456903203677865, -0.007912219312377842, + -0.00625202770436523, -0.00530785086116117, + -0.005569722659634311, -0.004664448462594344, + -0.0037747773914077747, -0.004175649656985592, + -0.004659601521384289, -0.005008602967819641, + -0.004730625079902729, -0.0034039554356604146, + -0.0017110333873406587, -0.0006091938771510242, + -0.0016051679050678297, -0.003312864664007262, + -0.004505512715977288, -0.004152222189861692, + -0.003218596419678823, -0.0027277806209877343, + -0.001715005444317267, -0.0012589960071233749, + -0.001852908777923165, -0.002540339553144362 +}; +/* Center rear (180 degree) */ +static const float cr_filt[128] = { + -0.00005989110716536726, -0.00022790291829128702, + 0.0002659166098971966, -0.0003774772716776257, + 0.0004540309551867803, -0.000420238187386368, + 0.00025518536450885686, 0.00028285526288953955, + -0.001016391007574093, 0.0028634984299063795, + 0.0021574799687976045, 0.01035121276682072, + -0.010481720917298163, -0.013197198495899292, + 0.0031928225328717195, 0.02903137843618603, + 0.1632429772511569, 0.1047487989875262, + -0.10464685060623742, -0.09260196288035998, + -0.007514241993554443, 0.013596249226741712, + -0.019876166508450258, 0.1255626123599804, + 0.3648170359521724, 0.19458249753223478, + -0.04434070930031298, 0.046582528121935265, + 0.09484427347230277, -0.03137795311969644, + -0.10297437925363695, -0.09351091015917065, + -0.1129521092162064, -0.14925322995658827, + -0.1231466295584665, -0.06356719756705227, + -0.05442277895126282, -0.07568433015661316, + -0.023314932828602003, 0.04260950721318558, + 0.02249026315598923, 0.02048195669571197, + 0.05651342117268278, 0.05885038917623213, + 0.03797102097397795, 0.011767394419953451, + 0.00560502503429481, 0.005051125343961189, + -0.012925933188033823, -0.023918884651306566, + -0.013251659441678816, -0.010694772488866284, + -0.03080486448617846, -0.03661278237783158, + -0.0379227303416262, -0.042189005718490775, + -0.026595666344596286, -0.009759025956801257, + -0.002064986663513004, -0.002420117028098389, + -0.006629991977552491, 0.004619970897631026, + 0.019450642967537877, 0.0173521119057514, + 0.017641425439988062, 0.02270029598048491, + 0.018976431925275348, 0.009299852902290885, + -0.001695039371619912, -0.00675162574265618, + -0.009380968871003034, -0.011208396125485165, + -0.01308640049201482, -0.0165636375633249, + -0.022004099870933345, -0.025173458684139286, + -0.016918759559175375, -0.00865150653575917, + -0.006999929082792643, -0.005454830010518988, + -0.0021129521131095317, 0.00018717090054046307, + -0.0002864344788569993, 0.0017615225381095569, + 0.006985907557802283, 0.010577308310476465, + 0.006466104789306027, -0.0014988738575948326, + -0.0039669755229277195, -0.0065156971200080235, + -0.009343206924192169, -0.0076430644693577495, + -0.004395214976600924, -0.003052735340422483, + -0.007019103043066595, -0.00974109267696527, + -0.007968015032797376, -0.007801513845528344, + -0.007535748903681969, -0.003543341967287925, + 0.0015083125553729722, 0.0023345972556147025, + -0.0010043623069557037, -0.0025295765105203746, + -0.0023701840891643634, -0.0005908186035024362, + 0.0029826252289082847, 0.004829048542117764, + 0.004488360022902081, 0.00002643748103005408, + -0.0042100779212597295, -0.006170600558114495, + -0.007267149164680168, -0.006825522903494639, + -0.006899834372739123, -0.0073493916110062675, + -0.009554351265163382, -0.011790297433830197, + -0.010645796603734424, -0.0064661575394022106, + -0.002026743466524137, -0.0004337034584909932, + -0.0011172647031654614, -0.0017947816283674731, + -0.00255615052036616, -0.0017721562881944813, + -0.0002379619297227554, 0.0007130120121089036 +}; + +#endif /* MPLAYER_AF_HRTF_H */ diff --git a/audio/filter/af_karaoke.c b/audio/filter/af_karaoke.c new file mode 100644 index 0000000000..965eb8f40d --- /dev/null +++ b/audio/filter/af_karaoke.c @@ -0,0 +1,98 @@ +/* + * simple voice removal filter + * + * copyright (c) 2006 Reynaldo H. Verdejo Pinochet + * Based on code by Alex Beregszaszi for his 'center' filter. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "af.h" + +// Data for specific instances of this filter + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT: + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + af->data->format= AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + return af_test_output(af,(struct mp_audio*)arg); + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + float* a = c->audio; // Audio data + int len = c->len/4; // Number of samples in current audio block + int nch = c->nch; // Number of channels + register int i; + + /* + FIXME1 add a low band pass filter to avoid suppressing + centered bass/drums + FIXME2 better calculated* attenuation factor + */ + + for(i=0;i<len;i+=nch) + { + a[i] = (a[i] - a[i+1]) * 0.7; + a[i+1]=a[i]; + } + + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control = control; + af->uninit = uninit; + af->play = play; + af->mul = 1; + af->data = calloc(1,sizeof(struct mp_audio)); + + if(af->data == NULL) + return AF_ERROR; + + return AF_OK; +} + +// Description of this filter +struct af_info af_info_karaoke = { + "Simple karaoke/voice-removal audio filter", + "karaoke", + "Reynaldo H. Verdejo Pinochet", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_ladspa.c b/audio/filter/af_ladspa.c new file mode 100644 index 0000000000..c1b3f24360 --- /dev/null +++ b/audio/filter/af_ladspa.c @@ -0,0 +1,915 @@ +/* + * LADSPA plugin loader + * + * Written by Ivo van Poorten <ivop@euronet.nl> + * Copyright (C) 2004, 2005 + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* ------------------------------------------------------------------------- */ + +/* Global Includes */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <inttypes.h> +#include <math.h> +#include <limits.h> + +#include <dlfcn.h> +#include <ladspa.h> + +/* ------------------------------------------------------------------------- */ + +/* Local Includes */ + +#include "af.h" + +/* ------------------------------------------------------------------------- */ + +/* Filter specific data */ + +typedef struct af_ladspa_s +{ + int status; /**< Status of the filter. + * Either AF_OK or AF_ERROR + * Because MPlayer re-inits audio filters that + * _clearly_ returned AF_ERROR anyway, I use this + * in play() to skip the processing and return + * the data unchanged. + */ + + int activated; /**< 0 or 1. Activate LADSPA filters only once, even + * if the buffers get resized, to avoid a stuttering + * filter. + */ + + char *file; + char *label; + + char *myname; /**< It's easy to have a concatenation of file and label */ + + void *libhandle; + const LADSPA_Descriptor *plugin_descriptor; + + int nports; + + int ninputs; + int *inputs; + + int noutputs; + int *outputs; + + int ninputcontrols; + int *inputcontrolsmap; /**< Map input port number [0-] to actual port */ + float *inputcontrols; + + int noutputcontrols; + int *outputcontrolsmap; + float *outputcontrols; + + int nch; /**< number of channels */ + int bufsize; + float **inbufs; + float **outbufs; + LADSPA_Handle *chhandles; + +} af_ladspa_t; + +/* ------------------------------------------------------------------------- */ + +static int af_open(struct af_instance *af); +static int af_ladspa_malloc_failed(char*); + +/* ------------------------------------------------------------------------- */ + +/* Description */ + +struct af_info af_info_ladspa = { + "LADSPA plugin loader", + "ladspa", + "Ivo van Poorten", + "", + AF_FLAGS_REENTRANT, + af_open +}; + +/* ------------------------------------------------------------------------- */ + +/* By lack of a better word (in my vocabulary) this is called 'parse'. + * Feel free to suggest an alternative. + */ + +/** \brief Check for inputs, outputs and controls of a given filter. + * + * This function counts and checks all input, output and control ports + * of the filter that was loaded. If it turns out to be a valid + * filter for MPlayer use, it prints out a list of all controls and + * the corresponding range of its value at message level MSGL_V. + * + * \param setup Current setup of the filter. Must have its + * plugin_descriptor set! + * + * \return Returns AF_OK if it has a valid input/output/controls + * configuration. Else, it returns AF_ERROR. + */ + +static int af_ladspa_parse_plugin(af_ladspa_t *setup) { + int p, i; + const LADSPA_Descriptor *pdes = setup->plugin_descriptor; + LADSPA_PortDescriptor d; + LADSPA_PortRangeHint hint; + + if (!setup->libhandle) + return AF_ERROR; /* only call parse after a succesful load */ + if (!setup->plugin_descriptor) + return AF_ERROR; /* same as above */ + + /* let's do it */ + + setup->nports = pdes->PortCount; + + /* allocate memory for all inputs/outputs/controls */ + + setup->inputs = calloc(setup->nports, sizeof(int)); + if (!setup->inputs) return af_ladspa_malloc_failed(setup->myname); + + setup->outputs = calloc(setup->nports, sizeof(int)); + if (!setup->outputs) return af_ladspa_malloc_failed(setup->myname); + + setup->inputcontrolsmap = calloc(setup->nports, sizeof(int)); + if (!setup->inputcontrolsmap) return af_ladspa_malloc_failed(setup->myname); + + setup->inputcontrols = calloc(setup->nports, sizeof(float)); + if (!setup->inputcontrols) return af_ladspa_malloc_failed(setup->myname); + + setup->outputcontrolsmap = calloc(setup->nports, sizeof(int)); + if (!setup->outputcontrolsmap) return af_ladspa_malloc_failed(setup->myname); + + setup->outputcontrols = calloc(setup->nports, sizeof(float)); + if (!setup->outputcontrols) return af_ladspa_malloc_failed(setup->myname); + + /* set counts to zero */ + + setup->ninputs = 0; + setup->noutputs = 0; + setup->ninputcontrols = 0; + setup->noutputcontrols = 0; + + /* check all ports, see what type it is and set variables according to + * what we have found + */ + + for (p=0; p<setup->nports; p++) { + d = pdes->PortDescriptors[p]; + + if (LADSPA_IS_PORT_AUDIO(d)) { + if (LADSPA_IS_PORT_INPUT(d)) { + setup->inputs[setup->ninputs] = p; + setup->ninputs++; + } else if (LADSPA_IS_PORT_OUTPUT(d)) { + setup->outputs[setup->noutputs] = p; + setup->noutputs++; + } + } + + if (LADSPA_IS_PORT_CONTROL(d)) { + if (LADSPA_IS_PORT_INPUT(d)) { + setup->inputcontrolsmap[setup->ninputcontrols] = p; + setup->ninputcontrols++; + /* set control to zero. set values after reading the rest + * of the suboptions and check LADSPA_?_HINT's later. + */ + setup->inputcontrols[p] = 0.0f; + } else if (LADSPA_IS_PORT_OUTPUT(d)) { + /* read and handle these too, otherwise filters that have them + * will sig11 + */ + setup->outputcontrolsmap[setup->noutputcontrols]=p; + setup->noutputcontrols++; + setup->outputcontrols[p] = 0.0f; + } + } + + } + + if (setup->ninputs == 0) { + mp_msg(MSGT_AFILTER, MSGL_WARN, "%s: %s\n", setup->myname, + _("WARNING! This LADSPA plugin has no audio inputs.\n The incoming audio signal will be lost.")); + } else if (setup->ninputs == 1) { + mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a mono effect\n", setup->myname); + } else if (setup->ninputs == 2) { + mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a stereo effect\n", setup->myname); + } else { + mp_msg(MSGT_AFILTER, MSGL_V, "%s: this is a %i-channel effect, " + "support is experimental\n", setup->myname, setup->ninputs); + } + + if (setup->noutputs == 0) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("This LADSPA plugin has no audio outputs.")); + return AF_ERROR; + } + + if (setup->noutputs != setup->ninputs ) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("The number of audio inputs and audio outputs of the LADSPA plugin differ.")); + return AF_ERROR; + } + + mp_msg(MSGT_AFILTER, MSGL_V, "%s: this plugin has %d input control(s)\n", + setup->myname, setup->ninputcontrols); + + /* Print list of controls and its range of values it accepts */ + + for (i=0; i<setup->ninputcontrols; i++) { + p = setup->inputcontrolsmap[i]; + hint = pdes->PortRangeHints[p]; + mp_msg(MSGT_AFILTER, MSGL_V, " --- %d %s [", i, pdes->PortNames[p]); + + if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) { + mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f , ", hint.LowerBound); + } else { + mp_msg(MSGT_AFILTER, MSGL_V, "... , "); + } + + if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) { + mp_msg(MSGT_AFILTER, MSGL_V, "%0.2f]\n", hint.UpperBound); + } else { + mp_msg(MSGT_AFILTER, MSGL_V, "...]\n"); + } + + } + + return AF_OK; +} + +/* ------------------------------------------------------------------------- */ + +/* This function might "slightly" look like dlopenLADSPA in the LADSPA SDK :-) + * But, I changed a few things, because imho it was broken. It did not support + * relative paths, only absolute paths that start with a / + * I think ../../some/dir/foobar.so is just as valid. And if one wants to call + * his library '...somename...so' he's crazy, but it should be allowed. + * So, search the path first, try plain *filename later. + * Also, try adding .so first! I like the recursion the SDK did, but it's + * better the other way around. -af ladspa=cmt:amp_stereo:0.5 is easier to type + * than -af ladspa=cmt.so:amp_stereo:0.5 :-)) + */ + +/** \brief dlopen() wrapper + * + * This is a wrapper around dlopen(). It tries various variations of the + * filename (with or without the addition of the .so extension) in various + * directories specified by the LADSPA_PATH environment variable. If all fails + * it tries the filename directly as an absolute path to the library. + * + * \param filename filename of the library to load. + * \param flag see dlopen(3) for a description of the flags. + * + * \return returns a pointer to the loaded library on success, or + * NULL if it fails to load. + */ + +static void* mydlopen(const char *filename, int flag) { + char *buf; + const char *end, *start, *ladspapath; + int endsinso, needslash; + size_t filenamelen; + void *result = NULL; + +#if defined(__MINGW32__) || defined(__CYGWIN__) + /* For Windows there's only absolute path support. + * If you have a Windows machine, feel free to fix this. + * (path separator, shared objects extension, et cetera). */ + mp_msg(MSGT_AFILTER, MSGL_V, "\ton windows, only absolute pathnames " + "are supported\n"); + mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename); + return dlopen(filename, flag); +#endif + + filenamelen = strlen(filename); + + endsinso = 0; + if (filenamelen > 3) + endsinso = (strcmp(filename+filenamelen-3, ".so") == 0); + if (!endsinso) { + buf=malloc(filenamelen+4); + strcpy(buf, filename); + strcat(buf, ".so"); + result=mydlopen(buf, flag); + free(buf); + } + + if (result) + return result; + + ladspapath=getenv("LADSPA_PATH"); + + if (ladspapath) { + + start=ladspapath; + while (*start != '\0') { + end=start; + while ( (*end != ':') && (*end != '\0') ) + end++; + + buf=malloc(filenamelen + 2 + (end-start) ); + if (end > start) + strncpy(buf, start, end-start); + needslash=0; + if (end > start) + if (*(end-1) != '/') { + needslash = 1; + buf[end-start] = '/'; + } + strcpy(buf+needslash+(end-start), filename); + + mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", buf); + result=dlopen(buf, flag); + + free(buf); + if (result) + return result; + + start = end; + if (*start == ':') + start++; + } /* end while there's still more in the path */ + } /* end if there's a ladspapath */ + + /* last resort, just open it again, so the dlerror() message is correct */ + mp_msg(MSGT_AFILTER, MSGL_V, "\ttrying %s\n", filename); + return dlopen(filename,flag); +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Load a LADSPA Plugin + * + * This function loads the LADSPA plugin specified by the file and label + * that are present in the setup variable. First, it loads the library. + * If it fails, it returns AF_ERROR. If not, it continues to look for the + * specified label. If it finds it, it sets the plugin_descriptor inside + * setup and returns AF_OK. If it doesn't, it returns AF_ERROR. Special case + * is a label called 'help'. In that case, it prints a list of all available + * labels (filters) in the library specified by file. + * + * \param setup Current setup of the filter. Contains filename and label. + * + * \return Either AF_ERROR or AF_OK, depending on the success of the operation. + */ + +static int af_ladspa_load_plugin(af_ladspa_t *setup) { + const LADSPA_Descriptor *ladspa_descriptor; + LADSPA_Descriptor_Function descriptor_function; + int i; + + /* load library */ + mp_msg(MSGT_AFILTER, MSGL_V, "%s: loading ladspa plugin library %s\n", + setup->myname, setup->file); + + setup->libhandle = mydlopen(setup->file, RTLD_NOW); + + if (!setup->libhandle) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s %s\n\t%s\n", setup->myname, + _("failed to load"), setup->file, dlerror() ); + return AF_ERROR; + } + + mp_msg(MSGT_AFILTER, MSGL_V, "%s: library found.\n", setup->myname); + + /* find descriptor function */ + dlerror(); + descriptor_function = (LADSPA_Descriptor_Function) dlsym (setup->libhandle, + "ladspa_descriptor"); + + if (!descriptor_function) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n\t%s\n", setup->myname, + _("Couldn't find ladspa_descriptor() function in the specified library file."), dlerror()); + return AF_ERROR; + } + + /* if label == help, list all labels in library and exit */ + + if (strcmp(setup->label, "help") == 0) { + mp_msg(MSGT_AFILTER, MSGL_INFO, "%s: %s %s:\n", setup->myname, + _("available labels in"), setup->file); + for (i=0; ; i++) { + ladspa_descriptor = descriptor_function(i); + if (ladspa_descriptor == NULL) { + return AF_ERROR; + } + mp_msg(MSGT_AFILTER, MSGL_INFO, " %-16s - %s (%lu)\n", + ladspa_descriptor->Label, + ladspa_descriptor->Name, + ladspa_descriptor->UniqueID); + } + } + + mp_msg(MSGT_AFILTER, MSGL_V, "%s: looking for label\n", setup->myname); + + /* find label in library */ + for (i=0; ; i++) { + ladspa_descriptor = descriptor_function(i); + if (ladspa_descriptor == NULL) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("Couldn't find label in plugin library.")); + return AF_ERROR; + } + if (strcmp(ladspa_descriptor->Label, setup->label) == 0) { + setup->plugin_descriptor = ladspa_descriptor; + mp_msg(MSGT_AFILTER, MSGL_V, "%s: %s found\n", setup->myname, + setup->label); + return AF_OK; + } + } + + return AF_OK; +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Print a malloc() failed error message. + * + * Generic function which can be called if a call to malloc(), calloc(), + * strdup(), et cetera, failed. It prints a message to the console and + * returns AF_ERROR. + * + * \return AF_ERROR + */ + +static int af_ladspa_malloc_failed(char *myname) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s", myname, "Memory allocation failed.\n"); + return AF_ERROR; +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Controls the filter. + * + * Control the behaviour of the filter. + * + * Commands: + * CONTROL_REINIT Sets the af structure with proper values for number + * of channels, rate, format, et cetera. + * CONTROL_COMMAND_LINE Parses the suboptions given to this filter + * through arg. It first parses the filename and + * the label. After that, it loads the filter + * and finds out its proprties. Then in continues + * parsing the controls given on the commandline, + * if any are needed. + * + * \param af Audio filter instance + * \param cmd The command to execute + * \param arg Arguments to the command + * + * \return Either AF_ERROR or AF_OK, depending on the succes of the + * operation. + */ + +static int control(struct af_instance *af, int cmd, void *arg) { + af_ladspa_t *setup = (af_ladspa_t*) af->setup; + int i, r; + float val; + + switch(cmd) { + case AF_CONTROL_REINIT: + mp_msg(MSGT_AFILTER, MSGL_V, "%s: (re)init\n", setup->myname); + + if (!arg) return AF_ERROR; + + /* accept FLOAT, let af_format do conversion */ + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + + /* arg->len is not set here yet, so init of buffers and connecting the + * filter, has to be done in play() :-/ + */ + + return af_test_output(af, (struct mp_audio*)arg); + case AF_CONTROL_COMMAND_LINE: { + char *buf; + char *line = arg; + + mp_msg(MSGT_AFILTER, MSGL_V, "%s: parse suboptions\n", setup->myname); + + /* suboption parser here! + * format is (ladspa=)file:label:controls.... + */ + + if (!line) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("No suboptions specified.")); + return AF_ERROR; + } + + buf = malloc(strlen(line)+1); + if (!buf) return af_ladspa_malloc_failed(setup->myname); + + /* file... */ + buf[0] = '\0'; + sscanf(line, "%[^:]", buf); + if (buf[0] == '\0') { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("No library file specified.")); + free(buf); + return AF_ERROR; + } + line += strlen(buf); + setup->file = strdup(buf); + if (!setup->file) return af_ladspa_malloc_failed(setup->myname); + mp_msg(MSGT_AFILTER, MSGL_V, "%s: file --> %s\n", setup->myname, + setup->file); + if (*line != '\0') line++; /* read ':' */ + + /* label... */ + buf[0] = '\0'; + sscanf(line, "%[^:]", buf); + if (buf[0] == '\0') { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("No filter label specified.")); + free(buf); + return AF_ERROR; + } + line += strlen(buf); + setup->label = strdup(buf); + if (!setup->label) return af_ladspa_malloc_failed(setup->myname); + mp_msg(MSGT_AFILTER, MSGL_V, "%s: label --> %s\n", setup->myname, + setup->label); +/* if (*line != '0') line++; */ /* read ':' */ + + free(buf); /* no longer needed */ + + /* set new setup->myname */ + + free(setup->myname); + setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+ + strlen(setup->label)+6, 1); + snprintf(setup->myname, strlen(af_info_ladspa.name)+ + strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)", + af_info_ladspa.name, setup->file, setup->label); + + /* load plugin :) */ + + if ( af_ladspa_load_plugin(setup) != AF_OK ) + return AF_ERROR; + + /* see what inputs, outputs and controls this plugin has */ + if ( af_ladspa_parse_plugin(setup) != AF_OK ) + return AF_ERROR; + + /* ninputcontrols is set by now, read control values from arg */ + + for(i=0; i<setup->ninputcontrols; i++) { + if (!line || *line != ':') { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("Not enough controls specified on the command line.")); + return AF_ERROR; + } + line++; + r = sscanf(line, "%f", &val); + if (r!=1) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname, + _("Not enough controls specified on the command line.")); + return AF_ERROR; + } + setup->inputcontrols[setup->inputcontrolsmap[i]] = val; + line = strchr(line, ':'); + } + + mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname); + for(i=0; i<setup->ninputcontrols; i++) { + mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ", + setup->inputcontrols[setup->inputcontrolsmap[i]]); + } + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + /* check boundaries of inputcontrols */ + + mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n", + setup->myname); + for(i=0; i<setup->ninputcontrols; i++) { + int p = setup->inputcontrolsmap[i]; + LADSPA_PortRangeHint hint = + setup->plugin_descriptor->PortRangeHints[p]; + val = setup->inputcontrols[p]; + + if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) && + val < hint.LowerBound) { + mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is below lower boundary of %0.4f.\n", + setup->myname, i, hint.LowerBound); + return AF_ERROR; + } + if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) && + val > hint.UpperBound) { + mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is above upper boundary of %0.4f.\n", + setup->myname, i, hint.UpperBound); + return AF_ERROR; + } + } + mp_msg(MSGT_AFILTER, MSGL_V, "%s: all controls have sane values\n", + setup->myname); + + /* All is well! */ + setup->status = AF_OK; + + return AF_OK; } + } + + return AF_UNKNOWN; +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Uninitialise the LADSPA Plugin Loader filter. + * + * This function deactivates the plugin(s), cleans up, frees all allocated + * memory and exits. + * + * \return No return value. + */ + +static void uninit(struct af_instance *af) { + int i; + + free(af->data); + if (af->setup) { + af_ladspa_t *setup = (af_ladspa_t*) af->setup; + const LADSPA_Descriptor *pdes = setup->plugin_descriptor; + + if (setup->myname) { + mp_msg(MSGT_AFILTER, MSGL_V, "%s: cleaning up\n", setup->myname); + free(setup->myname); + } + + if (setup->chhandles) { + for(i=0; i<setup->nch; i+=setup->ninputs) { + if (pdes->deactivate) pdes->deactivate(setup->chhandles[i]); + if (pdes->cleanup) pdes->cleanup(setup->chhandles[i]); + } + free(setup->chhandles); + } + + free(setup->file); + free(setup->label); + free(setup->inputcontrolsmap); + free(setup->inputcontrols); + free(setup->outputcontrolsmap); + free(setup->outputcontrols); + free(setup->inputs); + free(setup->outputs); + + if (setup->inbufs) { + for(i=0; i<setup->nch; i++) + free(setup->inbufs[i]); + free(setup->inbufs); + } + + if (setup->outbufs) { + for(i=0; i<setup->nch; i++) + free(setup->outbufs[i]); + free(setup->outbufs); + } + + if (setup->libhandle) + dlclose(setup->libhandle); + + free(setup); + setup = NULL; + } +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Process chunk of audio data through the selected LADSPA Plugin. + * + * \param af Pointer to audio filter instance + * \param data Pointer to chunk of audio data + * + * \return Either AF_ERROR or AF_OK + */ + +static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) { + af_ladspa_t *setup = af->setup; + const LADSPA_Descriptor *pdes = setup->plugin_descriptor; + float *audio = (float*)data->audio; + int nsamples = data->len/4; /* /4 because it's 32-bit float */ + int nch = data->nch; + int rate = data->rate; + int i, p; + + if (setup->status !=AF_OK) + return data; + + /* See if it's the first call. If so, setup inbufs/outbufs, instantiate + * plugin, connect ports and activate plugin + */ + + /* 2004-12-07: Also check if the buffersize has to be changed! + * data->len is not constant per se! re-init buffers. + */ + + if ( (setup->bufsize != nsamples/nch) || (setup->nch != nch) ) { + + /* if setup->nch==0, it's the first call, if not, something has + * changed and all previous mallocs have to be freed + */ + + if (setup->nch != 0) { + mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize change; free old buffer\n", + setup->myname); + + if(setup->inbufs) { + for(i=0; i<setup->nch; i++) + free(setup->inbufs[i]); + free(setup->inbufs); + } + if(setup->outbufs) { + for(i=0; i<setup->nch; i++) + free(setup->outbufs[i]); + free(setup->outbufs); + } + } /* everything is freed */ + + setup->bufsize = nsamples/nch; + setup->nch = nch; + + setup->inbufs = calloc(nch, sizeof(float*)); + setup->outbufs = calloc(nch, sizeof(float*)); + + mp_msg(MSGT_AFILTER, MSGL_DBG3, "%s: bufsize = %d\n", + setup->myname, setup->bufsize); + + for(i=0; i<nch; i++) { + setup->inbufs[i] = calloc(setup->bufsize, sizeof(float)); + setup->outbufs[i] = calloc(setup->bufsize, sizeof(float)); + } + + /* only on the first call, there are no handles. */ + + if (!setup->chhandles) { + setup->chhandles = calloc(nch, sizeof(LADSPA_Handle)); + + /* create handles + * for stereo effects, create one handle for two channels + */ + + for(i=0; i<nch; i++) { + + if (i % setup->ninputs) { /* stereo effect */ + /* copy the handle from previous channel */ + setup->chhandles[i] = setup->chhandles[i-1]; + continue; + } + + setup->chhandles[i] = pdes->instantiate(pdes, rate); + } + } + + /* connect input/output ports for each channel/filter instance + * + * always (re)connect ports + */ + + for(i=0; i<nch; i++) { + pdes->connect_port(setup->chhandles[i], + setup->inputs[i % setup->ninputs], + setup->inbufs[i]); + pdes->connect_port(setup->chhandles[i], + setup->outputs[i % setup->ninputs], + setup->outbufs[i]); + + /* connect (input) controls */ + + for (p=0; p<setup->nports; p++) { + LADSPA_PortDescriptor d = pdes->PortDescriptors[p]; + if (LADSPA_IS_PORT_CONTROL(d)) { + if (LADSPA_IS_PORT_INPUT(d)) { + pdes->connect_port(setup->chhandles[i], p, + &(setup->inputcontrols[p]) ); + } else { + pdes->connect_port(setup->chhandles[i], p, + &(setup->outputcontrols[p]) ); + } + } + } + + /* Activate filter (if it isn't already :) ) */ + + if (pdes->activate && !setup->activated && i % setup->ninputs == 0) + pdes->activate(setup->chhandles[i]); + + } /* All channels/filters done! except for... */ + setup->activated = 1; + + /* Stereo effect with one channel left. Use same buffer for left + * and right. connect it to the second port. + */ + + for (p = i; p % setup->ninputs; p++) { + pdes->connect_port(setup->chhandles[i-1], + setup->inputs[p % setup->ninputs], + setup->inbufs[i-1]); + pdes->connect_port(setup->chhandles[i-1], + setup->outputs[p % setup->ninputs], + setup->outbufs[i-1]); + } /* done! */ + + } /* setup for first call/change of bufsize is done. + * normal playing routine follows... + */ + + /* Right now, I use a separate input and output buffer. + * I could change this to in-place processing (inbuf==outbuf), but some + * ladspa filters are broken and are not able to handle that. This seems + * fast enough, so unless somebody complains, it stays this way :) + */ + + /* Fill inbufs */ + + for (p=0; p<setup->bufsize; p++) { + for (i=0; i<nch; i++) { + setup->inbufs[i][p] = audio[p*nch + i]; + } + } + + /* Run filter(s) */ + + for (i=0; i<nch; i+=setup->ninputs) { + pdes->run(setup->chhandles[i], setup->bufsize); + } + + /* Extract outbufs */ + + for (p=0; p<setup->bufsize; p++) { + for (i=0; i<nch; i++) { + audio[p*nch + i] = setup->outbufs[i][p]; + } + } + + /* done */ + + return data; +} + +/* ------------------------------------------------------------------------- */ + +/** \brief Open LADSPA Plugin Loader Filter + * + * \param af Audio Filter instance + * + * \return Either AF_ERROR or AF_OK + */ + +static int af_open(struct af_instance *af) { + + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + + af->data = calloc(1, sizeof(struct mp_audio)); + if (af->data == NULL) + return af_ladspa_malloc_failed((char*)af_info_ladspa.name); + + af->setup = calloc(1, sizeof(af_ladspa_t)); + if (af->setup == NULL) { + free(af->data); + af->data=NULL; + return af_ladspa_malloc_failed((char*)af_info_ladspa.name); + } + + ((af_ladspa_t*)af->setup)->status = AF_ERROR; /* will be set to AF_OK if + * all went OK and play() + * should proceed. + */ + + ((af_ladspa_t*)af->setup)->myname = strdup(af_info_ladspa.name); + if (!((af_ladspa_t*)af->setup)->myname) + return af_ladspa_malloc_failed((char*)af_info_ladspa.name); + + return AF_OK; +} + +/* ------------------------------------------------------------------------- */ diff --git a/audio/filter/af_lavcac3enc.c b/audio/filter/af_lavcac3enc.c new file mode 100644 index 0000000000..ad78266ad3 --- /dev/null +++ b/audio/filter/af_lavcac3enc.c @@ -0,0 +1,332 @@ +/* + * audio filter for runtime AC-3 encoding with libavcodec. + * + * Copyright (C) 2007 Ulion <ulion A gmail P com> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <assert.h> + +#include <libavcodec/avcodec.h> +#include <libavutil/intreadwrite.h> +#include <libavutil/mem.h> + +#include "config.h" +#include "af.h" +#include "reorder_ch.h" + + +#define AC3_MAX_CHANNELS 6 +#define AC3_MAX_CODED_FRAME_SIZE 3840 +#define AC3_FRAME_SIZE (6 * 256) +const uint16_t ac3_bitrate_tab[19] = { + 32, 40, 48, 56, 64, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 +}; + +// Data for specific instances of this filter +typedef struct af_ac3enc_s { + struct AVCodec *lavc_acodec; + struct AVCodecContext *lavc_actx; + int add_iec61937_header; + int bit_rate; + int pending_data_size; + char *pending_data; + int pending_len; + int expect_len; + int min_channel_num; + int in_sampleformat; +} af_ac3enc_t; + +// Initialization and runtime control +static int control(struct af_instance *af, int cmd, void *arg) +{ + af_ac3enc_t *s = af->setup; + struct mp_audio *data = arg; + int i, bit_rate, test_output_res; + static const int default_bit_rate[AC3_MAX_CHANNELS+1] = \ + {0, 96000, 192000, 256000, 384000, 448000, 448000}; + + switch (cmd){ + case AF_CONTROL_REINIT: + if (AF_FORMAT_IS_AC3(data->format) || data->nch < s->min_channel_num) + return AF_DETACH; + + af->data->format = s->in_sampleformat; + af->data->bps = af_fmt2bits(s->in_sampleformat) / 8; + if (data->rate == 48000 || data->rate == 44100 || data->rate == 32000) + af->data->rate = data->rate; + else + af->data->rate = 48000; + if (data->nch > AC3_MAX_CHANNELS) + af->data->nch = AC3_MAX_CHANNELS; + else + af->data->nch = data->nch; + test_output_res = af_test_output(af, data); + + s->pending_len = 0; + s->expect_len = AC3_FRAME_SIZE * data->nch * af->data->bps; + assert(s->expect_len <= s->pending_data_size); + if (s->add_iec61937_header) + af->mul = (double)AC3_FRAME_SIZE * 2 * 2 / s->expect_len; + else + af->mul = (double)AC3_MAX_CODED_FRAME_SIZE / s->expect_len; + + mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc reinit: %d, %d, %f, %d.\n", + data->nch, data->rate, af->mul, s->expect_len); + + bit_rate = s->bit_rate ? s->bit_rate : default_bit_rate[af->data->nch]; + + if (s->lavc_actx->channels != af->data->nch || + s->lavc_actx->sample_rate != af->data->rate || + s->lavc_actx->bit_rate != bit_rate) { + + avcodec_close(s->lavc_actx); + + // Put sample parameters + s->lavc_actx->channels = af->data->nch; + s->lavc_actx->sample_rate = af->data->rate; + s->lavc_actx->bit_rate = bit_rate; + + if (avcodec_open2(s->lavc_actx, s->lavc_acodec, NULL) < 0) { + mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Couldn't open codec %s, br=%d.\n", "ac3", bit_rate); + return AF_ERROR; + } + } + if (s->lavc_actx->frame_size != AC3_FRAME_SIZE) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "lavcac3enc: unexpected ac3 " + "encoder frame size %d\n", s->lavc_actx->frame_size); + return AF_ERROR; + } + af->data->format = AF_FORMAT_AC3_BE; + af->data->bps = 2; + af->data->nch = 2; + return test_output_res; + case AF_CONTROL_COMMAND_LINE: + mp_msg(MSGT_AFILTER, MSGL_DBG2, "af_lavcac3enc cmdline: %s.\n", (char*)arg); + s->bit_rate = 0; + s->min_channel_num = 0; + s->add_iec61937_header = 0; + sscanf(arg,"%d:%d:%d", &s->add_iec61937_header, &s->bit_rate, + &s->min_channel_num); + if (s->bit_rate < 1000) + s->bit_rate *= 1000; + if (s->bit_rate) { + for (i = 0; i < 19; ++i) + if (ac3_bitrate_tab[i] * 1000 == s->bit_rate) + break; + if (i >= 19) { + mp_msg(MSGT_AFILTER, MSGL_WARN, "af_lavcac3enc unable set unsupported " + "bitrate %d, use default bitrate (check manpage to see " + "supported bitrates).\n", s->bit_rate); + s->bit_rate = 0; + } + } + if (s->min_channel_num == 0) + s->min_channel_num = 5; + mp_msg(MSGT_AFILTER, MSGL_V, "af_lavcac3enc config spdif:%d, bitrate:%d, " + "minchnum:%d.\n", s->add_iec61937_header, s->bit_rate, + s->min_channel_num); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + if (af->data) + free(af->data->audio); + free(af->data); + if (af->setup) { + af_ac3enc_t *s = af->setup; + af->setup = NULL; + if(s->lavc_actx) { + avcodec_close(s->lavc_actx); + av_free(s->lavc_actx); + } + free(s->pending_data); + free(s); + } +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + af_ac3enc_t *s = af->setup; + struct mp_audio *c = data; // Current working data + struct mp_audio *l; + int len, left, outsize = 0, destsize; + char *buf, *src, *dest; + int max_output_len; + int frame_num = (data->len + s->pending_len) / s->expect_len; + int samplesize = af_fmt2bits(s->in_sampleformat) / 8; + + if (s->add_iec61937_header) + max_output_len = AC3_FRAME_SIZE * 2 * 2 * frame_num; + else + max_output_len = AC3_MAX_CODED_FRAME_SIZE * frame_num; + + if (af->data->len < max_output_len) { + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, " + "old len = %i, new len = %i\n", af->info->name, af->data->len, + max_output_len); + free(af->data->audio); + af->data->audio = malloc(max_output_len); + if (!af->data->audio) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory \n"); + return NULL; + } + af->data->len = max_output_len; + } + + l = af->data; // Local data + buf = l->audio; + src = c->audio; + left = c->len; + + + while (left > 0) { + if (left + s->pending_len < s->expect_len) { + memcpy(s->pending_data + s->pending_len, src, left); + src += left; + s->pending_len += left; + left = 0; + break; + } + + dest = s->add_iec61937_header ? buf + 8 : buf; + destsize = (char *)l->audio + l->len - buf; + + if (s->pending_len) { + int needs = s->expect_len - s->pending_len; + if (needs > 0) { + memcpy(s->pending_data + s->pending_len, src, needs); + src += needs; + left -= needs; + } + + if (c->nch >= 5) + reorder_channel_nch(s->pending_data, + AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, + AF_CHANNEL_LAYOUT_LAVC_DEFAULT, + c->nch, + s->expect_len / samplesize, samplesize); + + len = avcodec_encode_audio(s->lavc_actx, dest, destsize, + (void *)s->pending_data); + s->pending_len = 0; + } + else { + if (c->nch >= 5) + reorder_channel_nch(src, + AF_CHANNEL_LAYOUT_MPLAYER_DEFAULT, + AF_CHANNEL_LAYOUT_LAVC_DEFAULT, + c->nch, + s->expect_len / samplesize, samplesize); + len = avcodec_encode_audio(s->lavc_actx,dest,destsize,(void *)src); + src += s->expect_len; + left -= s->expect_len; + } + mp_msg(MSGT_AFILTER, MSGL_DBG2, "avcodec_encode_audio got %d, pending %d.\n", + len, s->pending_len); + + if (s->add_iec61937_header) { + int bsmod = dest[5] & 0x7; + + AV_WB16(buf, 0xF872); // iec 61937 syncword 1 + AV_WB16(buf + 2, 0x4E1F); // iec 61937 syncword 2 + buf[4] = bsmod; // bsmod + buf[5] = 0x01; // data-type ac3 + AV_WB16(buf + 6, len << 3); // number of bits in payload + + memset(buf + 8 + len, 0, AC3_FRAME_SIZE * 2 * 2 - 8 - len); + len = AC3_FRAME_SIZE * 2 * 2; + } + + outsize += len; + buf += len; + } + c->audio = l->audio; + c->nch = 2; + c->bps = 2; + c->len = outsize; + mp_msg(MSGT_AFILTER, MSGL_DBG2, "play return size %d, pending %d\n", + outsize, s->pending_len); + return c; +} + +static int af_open(struct af_instance* af){ + + af_ac3enc_t *s = calloc(1,sizeof(af_ac3enc_t)); + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=s; + + s->lavc_acodec = avcodec_find_encoder_by_name("ac3"); + if (!s->lavc_acodec) { + mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't find encoder for codec %s.\n", "ac3"); + return AF_ERROR; + } + + s->lavc_actx = avcodec_alloc_context3(s->lavc_acodec); + if (!s->lavc_actx) { + mp_tmsg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, couldn't allocate context!\n"); + return AF_ERROR; + } + const enum AVSampleFormat *fmts = s->lavc_acodec->sample_fmts; + for (int i = 0; ; i++) { + if (fmts[i] == AV_SAMPLE_FMT_NONE) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "Audio LAVC, encoder doesn't " + "support expected sample formats!\n"); + return AF_ERROR; + } else if (fmts[i] == AV_SAMPLE_FMT_S16) { + s->in_sampleformat = AF_FORMAT_S16_NE; + s->lavc_actx->sample_fmt = fmts[i]; + break; + } else if (fmts[i] == AV_SAMPLE_FMT_FLT) { + s->in_sampleformat = AF_FORMAT_FLOAT_NE; + s->lavc_actx->sample_fmt = fmts[i]; + break; + } + } + char buf[100]; + mp_msg(MSGT_AFILTER, MSGL_V, "[af_lavcac3enc]: in sample format: %s\n", + af_fmt2str(s->in_sampleformat, buf, 100)); + s->pending_data_size = AF_NCH * AC3_FRAME_SIZE * + af_fmt2bits(s->in_sampleformat) / 8; + s->pending_data = malloc(s->pending_data_size); + + return AF_OK; +} + +struct af_info af_info_lavcac3enc = { + "runtime encode to ac3 using libavcodec", + "lavcac3enc", + "Ulion", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_lavcresample.c b/audio/filter/af_lavcresample.c new file mode 100644 index 0000000000..ce777fed31 --- /dev/null +++ b/audio/filter/af_lavcresample.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> + +#include "config.h" +#include "af.h" +#include "libavcodec/avcodec.h" +#include "libavutil/rational.h" + +// Data for specific instances of this filter +typedef struct af_resample_s{ + struct AVResampleContext *avrctx; + int16_t *in[AF_NCH]; + int in_alloc; + int index; + + int filter_length; + int linear; + int phase_shift; + double cutoff; + + int ctx_out_rate; + int ctx_in_rate; + int ctx_filter_size; + int ctx_phase_shift; + int ctx_linear; + double ctx_cutoff; +}af_resample_t; + + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_resample_t* s = (af_resample_t*)af->setup; + struct mp_audio *data= (struct mp_audio*)arg; + int out_rate, test_output_res; // helpers for checking input format + + switch(cmd){ + case AF_CONTROL_REINIT: + if((af->data->rate == data->rate) || (af->data->rate == 0)) + return AF_DETACH; + + af->data->nch = data->nch; + if (af->data->nch > AF_NCH) af->data->nch = AF_NCH; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + af->mul = (double)af->data->rate / data->rate; + af->delay = af->data->nch * s->filter_length / min(af->mul, 1); // *bps*.5 + + if (s->ctx_out_rate != af->data->rate || s->ctx_in_rate != data->rate || s->ctx_filter_size != s->filter_length || + s->ctx_phase_shift != s->phase_shift || s->ctx_linear != s->linear || s->ctx_cutoff != s->cutoff) { + if(s->avrctx) av_resample_close(s->avrctx); + s->avrctx= av_resample_init(af->data->rate, /*in_rate*/data->rate, s->filter_length, s->phase_shift, s->linear, s->cutoff); + s->ctx_out_rate = af->data->rate; + s->ctx_in_rate = data->rate; + s->ctx_filter_size = s->filter_length; + s->ctx_phase_shift = s->phase_shift; + s->ctx_linear = s->linear; + s->ctx_cutoff = s->cutoff; + } + + // hack to make af_test_output ignore the samplerate change + out_rate = af->data->rate; + af->data->rate = data->rate; + test_output_res = af_test_output(af, (struct mp_audio*)arg); + af->data->rate = out_rate; + return test_output_res; + case AF_CONTROL_COMMAND_LINE:{ + s->cutoff= 0.0; + sscanf((char*)arg,"%d:%d:%d:%d:%lf", &af->data->rate, &s->filter_length, &s->linear, &s->phase_shift, &s->cutoff); + if(s->cutoff <= 0.0) s->cutoff= max(1.0 - 6.5/(s->filter_length+8), 0.80); + return AF_OK; + } + case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET: + af->data->rate = *(int*)arg; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + if(af->data) + free(af->data->audio); + free(af->data); + if(af->setup){ + int i; + af_resample_t *s = af->setup; + if(s->avrctx) av_resample_close(s->avrctx); + for (i=0; i < AF_NCH; i++) + free(s->in[i]); + free(s); + } +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + af_resample_t *s = af->setup; + int i, j, consumed, ret = 0; + int16_t *in = (int16_t*)data->audio; + int16_t *out; + int chans = data->nch; + int in_len = data->len/(2*chans); + int out_len = in_len * af->mul + 10; + int16_t tmp[AF_NCH][out_len]; + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + out= (int16_t*)af->data->audio; + + out_len= min(out_len, af->data->len/(2*chans)); + + if(s->in_alloc < in_len + s->index){ + s->in_alloc= in_len + s->index; + for(i=0; i<chans; i++){ + s->in[i]= realloc(s->in[i], s->in_alloc*sizeof(int16_t)); + } + } + + if(chans==1){ + memcpy(&s->in[0][s->index], in, in_len * sizeof(int16_t)); + }else if(chans==2){ + for(j=0; j<in_len; j++){ + s->in[0][j + s->index]= *(in++); + s->in[1][j + s->index]= *(in++); + } + }else{ + for(j=0; j<in_len; j++){ + for(i=0; i<chans; i++){ + s->in[i][j + s->index]= *(in++); + } + } + } + in_len += s->index; + + for(i=0; i<chans; i++){ + ret= av_resample(s->avrctx, tmp[i], s->in[i], &consumed, in_len, out_len, i+1 == chans); + } + out_len= ret; + + s->index= in_len - consumed; + for(i=0; i<chans; i++){ + memmove(s->in[i], s->in[i] + consumed, s->index*sizeof(int16_t)); + } + + if(chans==1){ + memcpy(out, tmp[0], out_len*sizeof(int16_t)); + }else if(chans==2){ + for(j=0; j<out_len; j++){ + *(out++)= tmp[0][j]; + *(out++)= tmp[1][j]; + } + }else{ + for(j=0; j<out_len; j++){ + for(i=0; i<chans; i++){ + *(out++)= tmp[i][j]; + } + } + } + + data->audio = af->data->audio; + data->len = out_len*chans*2; + data->rate = af->data->rate; + return data; +} + +static int af_open(struct af_instance* af){ + af_resample_t *s = calloc(1,sizeof(af_resample_t)); + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + s->filter_length= 16; + s->cutoff= max(1.0 - 6.5/(s->filter_length+8), 0.80); + s->phase_shift= 10; +// s->setup = RSMP_INT | FREQ_SLOPPY; + af->setup=s; + return AF_OK; +} + +struct af_info af_info_lavcresample = { + "Sample frequency conversion using libavcodec", + "lavcresample", + "Michael Niedermayer", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_pan.c b/audio/filter/af_pan.c new file mode 100644 index 0000000000..8b1783ee84 --- /dev/null +++ b/audio/filter/af_pan.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <inttypes.h> +#include <math.h> +#include <limits.h> + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_pan_s +{ + int nch; // Number of output channels; zero means same as input + float level[AF_NCH][AF_NCH]; // Gain level for each channel +}af_pan_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_pan_t* s = af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT: + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + af->data->nch = s->nch ? s->nch: ((struct mp_audio*)arg)->nch; + af->mul = (double)af->data->nch / ((struct mp_audio*)arg)->nch; + + if((af->data->format != ((struct mp_audio*)arg)->format) || + (af->data->bps != ((struct mp_audio*)arg)->bps)){ + ((struct mp_audio*)arg)->format = af->data->format; + ((struct mp_audio*)arg)->bps = af->data->bps; + return AF_FALSE; + } + return AF_OK; + case AF_CONTROL_COMMAND_LINE:{ + int nch = 0; + int n = 0; + char* cp = NULL; + int j,k; + // Read number of outputs + sscanf((char*)arg,"%i%n", &nch,&n); + if(AF_OK != control(af,AF_CONTROL_PAN_NOUT | AF_CONTROL_SET, &nch)) + return AF_ERROR; + + // Read pan values + cp = &((char*)arg)[n]; + j = 0; k = 0; + while((*cp == ':') && (k < AF_NCH)){ + sscanf(cp, ":%f%n" , &s->level[j][k], &n); + mp_msg(MSGT_AFILTER, MSGL_V, "[pan] Pan level from channel %i to" + " channel %i = %f\n",k,j,s->level[j][k]); + cp =&cp[n]; + j++; + if(j>=nch){ + j = 0; + k++; + } + } + return AF_OK; + } + case AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET:{ + int i; + int ch = ((af_control_ext_t*)arg)->ch; + float* level = ((af_control_ext_t*)arg)->arg; + if (ch >= AF_NCH) + return AF_FALSE; + for(i=0;i<AF_NCH;i++) + s->level[ch][i] = level[i]; + return AF_OK; + } + case AF_CONTROL_PAN_LEVEL | AF_CONTROL_GET:{ + int i; + int ch = ((af_control_ext_t*)arg)->ch; + float* level = ((af_control_ext_t*)arg)->arg; + if (ch >= AF_NCH) + return AF_FALSE; + for(i=0;i<AF_NCH;i++) + level[i] = s->level[ch][i]; + return AF_OK; + } + case AF_CONTROL_PAN_NOUT | AF_CONTROL_SET: + // Reinit must be called after this function has been called + + // Sanity check + if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[pan] The number of output channels must be" + " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]); + return AF_ERROR; + } + s->nch=((int*)arg)[0]; + return AF_OK; + case AF_CONTROL_PAN_NOUT | AF_CONTROL_GET: + *(int*)arg = af->data->nch; + return AF_OK; + case AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET:{ + float val = *(float*)arg; + if (s->nch) + return AF_ERROR; + if (af->data->nch >= 2) { + s->level[0][0] = min(1.f, 1.f - val); + s->level[0][1] = max(0.f, val); + s->level[1][0] = max(0.f, -val); + s->level[1][1] = min(1.f, 1.f + val); + } + return AF_OK; + } + case AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET: + if (s->nch) + return AF_ERROR; + *(float*)arg = s->level[0][1] - s->level[1][0]; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + if(af->data) + free(af->data->audio); + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + struct mp_audio* l = af->data; // Local data + af_pan_t* s = af->setup; // Setup for this instance + float* in = c->audio; // Input audio data + float* out = NULL; // Output audio data + float* end = in+c->len/4; // End of loop + int nchi = c->nch; // Number of input channels + int ncho = l->nch; // Number of output channels + register int j,k; + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + out = l->audio; + // Execute panning + // FIXME: Too slow + while(in < end){ + for(j=0;j<ncho;j++){ + register float x = 0.0; + register float* tin = in; + for(k=0;k<nchi;k++) + x += tin[k] * s->level[j][k]; + out[j] = x; + } + out+= ncho; + in+= nchi; + } + + // Set output data + c->audio = l->audio; + c->len = c->len / c->nch * l->nch; + c->nch = l->nch; + + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_pan_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_pan = { + "Panning audio filter", + "pan", + "Anders", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_resample.c b/audio/filter/af_resample.c new file mode 100644 index 0000000000..1f0b7cc942 --- /dev/null +++ b/audio/filter/af_resample.c @@ -0,0 +1,394 @@ +/* + * This audio filter changes the sample rate. + * + * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> + +#include "libavutil/common.h" +#include "libavutil/mathematics.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 filter length is chosen to 8 if the machine is + slow and to 16 if the machine is fast and has MMX. +*/ + +#if !HAVE_MMX // This machine is slow +#define L8 +#else +#define L16 +#endif + +#include "af_resample_template.c" + +// Filtering types +#define RSMP_LIN (0<<0) // Linear interpolation +#define RSMP_INT (1<<0) // 16 bit integer +#define RSMP_FLOAT (2<<0) // 32 bit floating point +#define RSMP_MASK (3<<0) + +// Defines for sloppy or exact resampling +#define FREQ_SLOPPY (0<<2) +#define FREQ_EXACT (1<<2) +#define FREQ_MASK (1<<2) + +// Accuracy for linear interpolation +#define STEPACCURACY 32 + +// local data +typedef struct af_resample_s +{ + void* w; // Current filter weights + void** xq; // Circular buffers + uint32_t xi; // Index for circular buffers + uint32_t wi; // Index for w + uint32_t i; // Number of new samples to put in x queue + uint32_t dn; // Down sampling factor + uint32_t up; // Up sampling factor + uint64_t step; // Step size for linear interpolation + uint64_t pt; // Pointer remainder for linear interpolation + int setup; // Setup parameters cmdline or through postcreate +} af_resample_t; + +// Fast linear interpolation resample with modest audio quality +static int linint(struct mp_audio* c,struct mp_audio* l, af_resample_t* s) +{ + uint32_t len = 0; // Number of input samples + uint32_t nch = l->nch; // Words pre transfer + uint64_t step = s->step; + int16_t* in16 = ((int16_t*)c->audio); + int16_t* out16 = ((int16_t*)l->audio); + int32_t* in32 = ((int32_t*)c->audio); + int32_t* out32 = ((int32_t*)l->audio); + uint64_t end = ((((uint64_t)c->len)/2LL)<<STEPACCURACY); + uint64_t pt = s->pt; + uint16_t tmp; + + switch (nch){ + case 1: + while(pt < end){ + out16[len++]=in16[pt>>STEPACCURACY]; + pt+=step; + } + s->pt=pt & ((1LL<<STEPACCURACY)-1); + break; + case 2: + end/=2; + while(pt < end){ + out32[len++]=in32[pt>>STEPACCURACY]; + pt+=step; + } + len=(len<<1); + s->pt=pt & ((1LL<<STEPACCURACY)-1); + break; + default: + end /=nch; + while(pt < end){ + tmp=nch; + do { + tmp--; + out16[len+tmp]=in16[tmp+(pt>>STEPACCURACY)*nch]; + } while (tmp); + len+=nch; + pt+=step; + } + s->pt=pt & ((1LL<<STEPACCURACY)-1); + } + return len; +} + +/* Determine resampling type and format */ +static int set_types(struct af_instance* af, struct mp_audio* data) +{ + af_resample_t* s = af->setup; + int rv = AF_OK; + float rd = 0; + + // Make sure this filter isn't redundant + if((af->data->rate == data->rate) || (af->data->rate == 0)) + return AF_DETACH; + /* If sloppy and small resampling difference (2%) */ + rd = abs((float)af->data->rate - (float)data->rate)/(float)data->rate; + if((((s->setup & FREQ_MASK) == FREQ_SLOPPY) && (rd < 0.02) && + (data->format != (AF_FORMAT_FLOAT_NE))) || + ((s->setup & RSMP_MASK) == RSMP_LIN)){ + s->setup = (s->setup & ~RSMP_MASK) | RSMP_LIN; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + mp_msg(MSGT_AFILTER, MSGL_V, "[resample] Using linear interpolation. \n"); + } + else{ + /* If the input format is float or if float is explicitly selected + use float, otherwise use int */ + if((data->format == (AF_FORMAT_FLOAT_NE)) || + ((s->setup & RSMP_MASK) == RSMP_FLOAT)){ + s->setup = (s->setup & ~RSMP_MASK) | RSMP_FLOAT; + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + } + else{ + s->setup = (s->setup & ~RSMP_MASK) | RSMP_INT; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + } + mp_msg(MSGT_AFILTER, MSGL_V, "[resample] Using %s processing and %s frequecy" + " conversion.\n", + ((s->setup & RSMP_MASK) == RSMP_FLOAT)?"floating point":"integer", + ((s->setup & FREQ_MASK) == FREQ_SLOPPY)?"inexact":"exact"); + } + + if(af->data->format != data->format || af->data->bps != data->bps) + rv = AF_FALSE; + data->format = af->data->format; + data->bps = af->data->bps; + af->data->nch = data->nch; + return rv; +} + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + switch(cmd){ + case AF_CONTROL_REINIT:{ + af_resample_t* s = af->setup; + struct mp_audio* n = arg; // New configuration + int i,d = 0; + int rv = AF_OK; + + // Free space for circular buffers + if(s->xq){ + free(s->xq[0]); + free(s->xq); + s->xq = NULL; + } + + if(AF_DETACH == (rv = set_types(af,n))) + return AF_DETACH; + + // If linear interpolation + if((s->setup & RSMP_MASK) == RSMP_LIN){ + s->pt=0LL; + s->step=((uint64_t)n->rate<<STEPACCURACY)/(uint64_t)af->data->rate+1LL; + mp_msg(MSGT_AFILTER, MSGL_DBG2, "[resample] Linear interpolation step: 0x%016"PRIX64".\n", + s->step); + af->mul = (double)af->data->rate / n->rate; + return rv; + } + + // Calculate up and down sampling factors + d=av_gcd(af->data->rate,n->rate); + + // If sloppy resampling is enabled limit the upsampling factor + if(((s->setup & FREQ_MASK) == FREQ_SLOPPY) && (af->data->rate/d > 5000)){ + int up=af->data->rate/2; + int dn=n->rate/2; + int m=2; + while(af->data->rate/(d*m) > 5000){ + d=av_gcd(up,dn); + up/=2; dn/=2; m*=2; + } + d*=m; + } + + // Create space for circular buffers + s->xq = malloc(n->nch*sizeof(void*)); + s->xq[0] = calloc(n->nch, 2*L*af->data->bps); + for(i=1;i<n->nch;i++) + s->xq[i] = (uint8_t *)s->xq[i-1] + 2*L*af->data->bps; + s->xi = 0; + + // Check if 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; + s->wi = 0; + s->i = 0; + + // Calculate cutoff frequency for filter + fc = 1/(float)(max(s->up,s->dn)); + // Allocate space for polyphase filter bank and prototype filter + w = malloc(sizeof(float) * s->up *L); + free(s->w); + s->w = malloc(L*s->up*af->data->bps); + + // Design prototype filter type using Kaiser window with beta = 10 + if(NULL == w || NULL == s->w || + -1 == af_filter_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 + if((s->setup & RSMP_MASK) == RSMP_INT){ + float t=(float)s->up*32767.0*(*wt); + ((int16_t*)s->w)[i*L+j] = (int16_t)((t>=0.0)?(t+0.5):(t-0.5)); + } + else + ((float*)s->w)[i*L+j] = (float)s->up*(*wt); + wt++; + } + } + free(w); + mp_msg(MSGT_AFILTER, MSGL_V, "[resample] New filter designed up: %i " + "down: %i\n", s->up, s->dn); + } + + // Set multiplier and delay + af->delay = 0; // not set correctly, but shouldn't be too large anyway + af->mul = (double)s->up / s->dn; + return rv; + } + case AF_CONTROL_COMMAND_LINE:{ + af_resample_t* s = af->setup; + int rate=0; + int type=RSMP_INT; + int sloppy=1; + sscanf((char*)arg,"%i:%i:%i", &rate, &sloppy, &type); + s->setup = (sloppy?FREQ_SLOPPY:FREQ_EXACT) | + (clamp(type,RSMP_LIN,RSMP_FLOAT)); + return af->control(af,AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET, &rate); + } + case AF_CONTROL_POST_CREATE: + if((((struct af_cfg*)arg)->force & AF_INIT_FORMAT_MASK) == AF_INIT_FLOAT) + ((af_resample_t*)af->setup)->setup = RSMP_FLOAT; + return AF_OK; + case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET: + // 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_V, "[resample] Changing sample rate " + "to %iHz\n",af->data->rate); + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + af_resample_t *s = af->setup; + if (s) { + if (s->xq) free(s->xq[0]); + free(s->xq); + free(s->w); + free(s); + } + if(af->data) + free(af->data->audio); + free(af->data); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + int len = 0; // Length of output data + struct mp_audio* c = data; // Current working data + struct mp_audio* l = af->data; // Local data + af_resample_t* s = af->setup; + + if(AF_OK != RESIZE_LOCAL_BUFFER(af,data)) + return NULL; + + // Run resampling + switch(s->setup & RSMP_MASK){ + case(RSMP_INT): +# define FORMAT_I 1 + if(s->up>s->dn){ +# define UP +# include "af_resample_template.c" +# undef UP + } + else{ +# define DN +# include "af_resample_template.c" +# undef DN + } + break; + case(RSMP_FLOAT): +# undef FORMAT_I +# define FORMAT_F 1 + if(s->up>s->dn){ +# define UP +# include "af_resample_template.c" +# undef UP + } + else{ +# define DN +# include "af_resample_template.c" +# undef DN + } + break; + case(RSMP_LIN): + len = linint(c, l, s); + break; + } + + // Set output data + c->audio = l->audio; + c->len = len*l->bps; + c->rate = l->rate; + + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_resample_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + ((af_resample_t*)af->setup)->setup = RSMP_INT | FREQ_SLOPPY; + return AF_OK; +} + +// Description of this plugin +struct af_info af_info_resample = { + "Sample frequency conversion", + "resample", + "Anders", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_resample_template.c b/audio/filter/af_resample_template.c new file mode 100644 index 0000000000..4d4c5922ca --- /dev/null +++ b/audio/filter/af_resample_template.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* This file contains the resampling engine, the sample format is + controlled by the FORMAT parameter, the filter length by the L + parameter and the resampling type by UP and DN. This file should + only be included by af_resample.c +*/ + +#undef L +#undef SHIFT +#undef FORMAT +#undef FIR +#undef ADDQUE + +/* The length Lxx definition selects the length of each poly phase + component. Valid definitions are L8 and L16 where the number + defines the nuber of taps. This definition affects the + computational complexity, the performance and the memory usage. +*/ + +/* The FORMAT_x parameter selects the sample format type currently + float and int16 are supported. Thes two formats are selected by + defining eiter FORMAT_F or FORMAT_I. The advantage of using float + is that the amplitude and therefore the SNR isn't affected by the + filtering, the disadvantage is that it is a lot slower. +*/ + +#if defined(FORMAT_I) +#define SHIFT >>16 +#define FORMAT int16_t +#else +#define SHIFT +#define FORMAT float +#endif + +// Short filter +#if defined(L8) + +#define L 8 // Filter length +// Unrolled loop to speed up execution +#define FIR(x,w,y) \ + (y[0]) = ( w[0]*x[0]+w[1]*x[1]+w[2]*x[2]+w[3]*x[3] \ + + w[4]*x[4]+w[5]*x[5]+w[6]*x[6]+w[7]*x[7] ) SHIFT + + + +#else /* L8/L16 */ + +#define L 16 +// Unrolled loop to speed up execution +#define FIR(x,w,y) \ + y[0] = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \ + + w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \ + + w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \ + + w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] ) SHIFT + +#endif /* L8/L16 */ + +// Macro to add data to circular que +#define ADDQUE(xi,xq,in)\ + xq[xi]=xq[(xi)+L]=*(in);\ + xi=((xi)-1)&(L-1); + +#if defined(UP) + + uint32_t ci = l->nch; // Index for channels + uint32_t nch = l->nch; // Number of channels + uint32_t inc = s->up/s->dn; + uint32_t level = s->up%s->dn; + uint32_t up = s->up; + uint32_t dn = s->dn; + uint32_t ns = c->len/l->bps; + register FORMAT* w = s->w; + + register uint32_t wi = 0; + register uint32_t xi = 0; + + // Index current channel + while(ci--){ + // Temporary pointers + register FORMAT* x = s->xq[ci]; + register FORMAT* in = ((FORMAT*)c->audio)+ci; + register FORMAT* out = ((FORMAT*)l->audio)+ci; + FORMAT* end = in+ns; // Block loop end + wi = s->wi; xi = s->xi; + + while(in < end){ + register uint32_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; +#endif /* UP */ + +#if defined(DN) /* DN */ + uint32_t ci = l->nch; // Index for channels + uint32_t nch = l->nch; // Number of channels + uint32_t inc = s->dn/s->up; + uint32_t level = s->dn%s->up; + uint32_t up = s->up; + uint32_t dn = s->dn; + uint32_t ns = c->len/l->bps; + FORMAT* w = s->w; + + register int32_t i = 0; + register uint32_t wi = 0; + register uint32_t xi = 0; + + // Index current channel + while(ci--){ + // Temporary pointers + register FORMAT* x = s->xq[ci]; + register FORMAT* in = ((FORMAT*)c->audio)+ci; + register FORMAT* out = ((FORMAT*)l->audio)+ci; + register FORMAT* end = in+ns; // Block loop end + i = s->i; wi = s->wi; xi = s->xi; + + while(in < end){ + + ADDQUE(xi,x,in); + in+=nch; + if((--i)<=0){ + // 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; + + // 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; +#endif /* DN */ diff --git a/audio/filter/af_scaletempo.c b/audio/filter/af_scaletempo.c new file mode 100644 index 0000000000..0bbc220997 --- /dev/null +++ b/audio/filter/af_scaletempo.c @@ -0,0 +1,581 @@ +/* + * scaletempo audio filter + * + * scale tempo while maintaining pitch + * (WSOLA technique with cross correlation) + * inspired by SoundTouch library by Olli Parviainen + * + * basic algorithm + * - produce 'stride' output samples per loop + * - consume stride*scale input samples per loop + * + * to produce smoother transitions between strides, blend next overlap + * samples from last stride with correlated samples of current input + * + * Copyright (c) 2007 Robert Juliano + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <assert.h> + +#include "af.h" +#include "libavutil/common.h" +#include "subopt-helper.h" + +// Data for specific instances of this filter +typedef struct af_scaletempo_s +{ + // stride + float scale; + float speed; + float frames_stride_scaled; + float frames_stride_error; + int bytes_per_frame; + int bytes_stride; + float bytes_stride_scaled; + int bytes_queue; + int bytes_queued; + int bytes_to_slide; + int8_t* buf_queue; + // overlap + int samples_overlap; + int samples_standing; + int bytes_overlap; + int bytes_standing; + void* buf_overlap; + void* table_blend; + void (*output_overlap)(struct af_scaletempo_s* s, void* out_buf, int bytes_off); + // best overlap + int frames_search; + int num_channels; + void* buf_pre_corr; + void* table_window; + int (*best_overlap_offset)(struct af_scaletempo_s* s); + // command line + float scale_nominal; + float ms_stride; + float percent_overlap; + float ms_search; + short speed_tempo; + short speed_pitch; +} af_scaletempo_t; + +static int fill_queue(struct af_instance* af, struct mp_audio* data, int offset) +{ + af_scaletempo_t* s = af->setup; + int bytes_in = data->len - offset; + int offset_unchanged = offset; + + if (s->bytes_to_slide > 0) { + if (s->bytes_to_slide < s->bytes_queued) { + int bytes_move = s->bytes_queued - s->bytes_to_slide; + memmove(s->buf_queue, + s->buf_queue + s->bytes_to_slide, + bytes_move); + s->bytes_to_slide = 0; + s->bytes_queued = bytes_move; + } else { + int bytes_skip; + s->bytes_to_slide -= s->bytes_queued; + bytes_skip = FFMIN(s->bytes_to_slide, bytes_in); + s->bytes_queued = 0; + s->bytes_to_slide -= bytes_skip; + offset += bytes_skip; + bytes_in -= bytes_skip; + } + } + + if (bytes_in > 0) { + int bytes_copy = FFMIN(s->bytes_queue - s->bytes_queued, bytes_in); + assert(bytes_copy >= 0); + memcpy(s->buf_queue + s->bytes_queued, + (int8_t*)data->audio + offset, + bytes_copy); + s->bytes_queued += bytes_copy; + offset += bytes_copy; + } + + return offset - offset_unchanged; +} + +#define UNROLL_PADDING (4*4) + +static int best_overlap_offset_float(af_scaletempo_t* s) +{ + float *pw, *po, *ppc, *search_start; + float best_corr = INT_MIN; + int best_off = 0; + int i, off; + + pw = s->table_window; + po = s->buf_overlap; + po += s->num_channels; + ppc = s->buf_pre_corr; + for (i=s->num_channels; i<s->samples_overlap; i++) { + *ppc++ = *pw++ * *po++; + } + + search_start = (float*)s->buf_queue + s->num_channels; + for (off=0; off<s->frames_search; off++) { + float corr = 0; + float* ps = search_start; + ppc = s->buf_pre_corr; + for (i=s->num_channels; i<s->samples_overlap; i++) { + corr += *ppc++ * *ps++; + } + if (corr > best_corr) { + best_corr = corr; + best_off = off; + } + search_start += s->num_channels; + } + + return best_off * 4 * s->num_channels; +} + +static int best_overlap_offset_s16(af_scaletempo_t* s) +{ + int32_t *pw, *ppc; + int16_t *po, *search_start; + int64_t best_corr = INT64_MIN; + int best_off = 0; + int off; + long i; + + pw = s->table_window; + po = s->buf_overlap; + po += s->num_channels; + ppc = s->buf_pre_corr; + for (i=s->num_channels; i<s->samples_overlap; i++) { + *ppc++ = ( *pw++ * *po++ ) >> 15; + } + + search_start = (int16_t*)s->buf_queue + s->num_channels; + for (off=0; off<s->frames_search; off++) { + int64_t corr = 0; + int16_t* ps = search_start; + ppc = s->buf_pre_corr; + ppc += s->samples_overlap - s->num_channels; + ps += s->samples_overlap - s->num_channels; + i = -(s->samples_overlap - s->num_channels); + do { + corr += ppc[i+0] * ps[i+0]; + corr += ppc[i+1] * ps[i+1]; + corr += ppc[i+2] * ps[i+2]; + corr += ppc[i+3] * ps[i+3]; + i += 4; + } while (i < 0); + if (corr > best_corr) { + best_corr = corr; + best_off = off; + } + search_start += s->num_channels; + } + + return best_off * 2 * s->num_channels; +} + +static void output_overlap_float(af_scaletempo_t* s, void* buf_out, + int bytes_off) +{ + float* pout = buf_out; + float* pb = s->table_blend; + float* po = s->buf_overlap; + float* pin = (float*)(s->buf_queue + bytes_off); + int i; + for (i=0; i<s->samples_overlap; i++) { + *pout++ = *po - *pb++ * ( *po - *pin++ ); po++; + } +} +static void output_overlap_s16(af_scaletempo_t* s, void* buf_out, + int bytes_off) +{ + int16_t* pout = buf_out; + int32_t* pb = s->table_blend; + int16_t* po = s->buf_overlap; + int16_t* pin = (int16_t*)(s->buf_queue + bytes_off); + int i; + for (i=0; i<s->samples_overlap; i++) { + *pout++ = *po - ( ( *pb++ * ( *po - *pin++ ) ) >> 16 ); po++; + } +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + af_scaletempo_t* s = af->setup; + int offset_in; + int max_bytes_out; + int8_t* pout; + + if (s->scale == 1.0) { + af->delay = 0; + return data; + } + + // RESIZE_LOCAL_BUFFER - can't use macro + max_bytes_out = ((int)(data->len / s->bytes_stride_scaled) + 1) * s->bytes_stride; + if (max_bytes_out > af->data->len) { + mp_msg(MSGT_AFILTER, MSGL_V, "[libaf] Reallocating memory in module %s, " + "old len = %i, new len = %i\n",af->info->name,af->data->len,max_bytes_out); + af->data->audio = realloc(af->data->audio, max_bytes_out); + if (!af->data->audio) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[libaf] Could not allocate memory\n"); + return NULL; + } + af->data->len = max_bytes_out; + } + + offset_in = fill_queue(af, data, 0); + pout = af->data->audio; + while (s->bytes_queued >= s->bytes_queue) { + int ti; + float tf; + int bytes_off = 0; + + // output stride + if (s->output_overlap) { + if (s->best_overlap_offset) + bytes_off = s->best_overlap_offset(s); + s->output_overlap(s, pout, bytes_off); + } + memcpy(pout + s->bytes_overlap, + s->buf_queue + bytes_off + s->bytes_overlap, + s->bytes_standing); + pout += s->bytes_stride; + + // input stride + memcpy(s->buf_overlap, + s->buf_queue + bytes_off + s->bytes_stride, + s->bytes_overlap); + tf = s->frames_stride_scaled + s->frames_stride_error; + ti = (int)tf; + s->frames_stride_error = tf - ti; + s->bytes_to_slide = ti * s->bytes_per_frame; + + offset_in += fill_queue(af, data, offset_in); + } + + // This filter can have a negative delay when scale > 1: + // output corresponding to some length of input can be decided and written + // after receiving only a part of that input. + af->delay = s->bytes_queued - s->bytes_to_slide; + + data->audio = af->data->audio; + data->len = pout - (int8_t *)af->data->audio; + return data; +} + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_scaletempo_t* s = af->setup; + switch(cmd){ + case AF_CONTROL_REINIT:{ + struct mp_audio* data = (struct mp_audio*)arg; + float srate = data->rate / 1000; + int nch = data->nch; + int bps; + int use_int = 0; + int frames_stride, frames_overlap; + int i, j; + + mp_msg(MSGT_AFILTER, MSGL_V, + "[scaletempo] %.3f speed * %.3f scale_nominal = %.3f\n", + s->speed, s->scale_nominal, s->scale); + + if (s->scale == 1.0) { + if (s->speed_tempo && s->speed_pitch) + return AF_DETACH; + memcpy(af->data, data, sizeof(struct mp_audio)); + af->delay = 0; + af->mul = 1; + return af_test_output(af, data); + } + + af->data->rate = data->rate; + af->data->nch = data->nch; + if ( data->format == AF_FORMAT_S16_LE + || data->format == AF_FORMAT_S16_BE ) { + use_int = 1; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = bps = 2; + } else { + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = bps = 4; + } + + frames_stride = srate * s->ms_stride; + s->bytes_stride = frames_stride * bps * nch; + s->bytes_stride_scaled = s->scale * s->bytes_stride; + s->frames_stride_scaled = s->scale * frames_stride; + s->frames_stride_error = 0; + af->mul = (double)s->bytes_stride / s->bytes_stride_scaled; + af->delay = 0; + + frames_overlap = frames_stride * s->percent_overlap; + if (frames_overlap <= 0) { + s->bytes_standing = s->bytes_stride; + s->samples_standing = s->bytes_standing / bps; + s->output_overlap = NULL; + s->bytes_overlap = 0; + } else { + s->samples_overlap = frames_overlap * nch; + s->bytes_overlap = frames_overlap * nch * bps; + s->bytes_standing = s->bytes_stride - s->bytes_overlap; + s->samples_standing = s->bytes_standing / bps; + s->buf_overlap = realloc(s->buf_overlap, s->bytes_overlap); + s->table_blend = realloc(s->table_blend, s->bytes_overlap * 4); + if(!s->buf_overlap || !s->table_blend) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n"); + return AF_ERROR; + } + memset(s->buf_overlap, 0, s->bytes_overlap); + if (use_int) { + int32_t* pb = s->table_blend; + int64_t blend = 0; + for (i=0; i<frames_overlap; i++) { + int32_t v = blend / frames_overlap; + for (j=0; j<nch; j++) { + *pb++ = v; + } + blend += 65536; // 2^16 + } + s->output_overlap = output_overlap_s16; + } else { + float* pb = s->table_blend; + for (i=0; i<frames_overlap; i++) { + float v = i / (float)frames_overlap; + for (j=0; j<nch; j++) { + *pb++ = v; + } + } + s->output_overlap = output_overlap_float; + } + } + + s->frames_search = (frames_overlap > 1) ? srate * s->ms_search : 0; + if (s->frames_search <= 0) { + s->best_overlap_offset = NULL; + } else { + if (use_int) { + int64_t t = frames_overlap; + int32_t n = 8589934588LL / (t * t); // 4 * (2^31 - 1) / t^2 + int32_t* pw; + s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap * 2 + UNROLL_PADDING); + s->table_window = realloc(s->table_window, s->bytes_overlap * 2 - nch * bps * 2); + if(!s->buf_pre_corr || !s->table_window) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n"); + return AF_ERROR; + } + memset((char *)s->buf_pre_corr + s->bytes_overlap * 2, 0, UNROLL_PADDING); + pw = s->table_window; + for (i=1; i<frames_overlap; i++) { + int32_t v = ( i * (t - i) * n ) >> 15; + for (j=0; j<nch; j++) { + *pw++ = v; + } + } + s->best_overlap_offset = best_overlap_offset_s16; + } else { + float* pw; + s->buf_pre_corr = realloc(s->buf_pre_corr, s->bytes_overlap); + s->table_window = realloc(s->table_window, s->bytes_overlap - nch * bps); + if(!s->buf_pre_corr || !s->table_window) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n"); + return AF_ERROR; + } + pw = s->table_window; + for (i=1; i<frames_overlap; i++) { + float v = i * (frames_overlap - i); + for (j=0; j<nch; j++) { + *pw++ = v; + } + } + s->best_overlap_offset = best_overlap_offset_float; + } + } + + s->bytes_per_frame = bps * nch; + s->num_channels = nch; + + s->bytes_queue + = (s->frames_search + frames_stride + frames_overlap) * bps * nch; + s->buf_queue = realloc(s->buf_queue, s->bytes_queue + UNROLL_PADDING); + if(!s->buf_queue) { + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[scaletempo] Out of memory\n"); + return AF_ERROR; + } + + s->bytes_queued = 0; + s->bytes_to_slide = 0; + + mp_msg (MSGT_AFILTER, MSGL_DBG2, "[scaletempo] " + "%.2f stride_in, %i stride_out, %i standing, " + "%i overlap, %i search, %i queue, %s mode\n", + s->frames_stride_scaled, + (int)(s->bytes_stride / nch / bps), + (int)(s->bytes_standing / nch / bps), + (int)(s->bytes_overlap / nch / bps), + s->frames_search, + (int)(s->bytes_queue / nch / bps), + (use_int?"s16":"float")); + + return af_test_output(af, (struct mp_audio*)arg); + } + case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:{ + if (s->speed_tempo) { + if (s->speed_pitch) { + break; + } + s->speed = *(float*)arg; + s->scale = s->speed * s->scale_nominal; + } else { + if (s->speed_pitch) { + s->speed = 1 / *(float*)arg; + s->scale = s->speed * s->scale_nominal; + break; + } + } + return AF_OK; + } + case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:{ + s->scale = *(float*)arg; + s->scale = s->speed * s->scale_nominal; + return AF_OK; + } + case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET: + *(float*)arg = s->scale; + return AF_OK; + case AF_CONTROL_COMMAND_LINE:{ + strarg_t speed = {}; + opt_t subopts[] = { + {"scale", OPT_ARG_FLOAT, &s->scale_nominal, NULL}, + {"stride", OPT_ARG_FLOAT, &s->ms_stride, NULL}, + {"overlap", OPT_ARG_FLOAT, &s->percent_overlap, NULL}, + {"search", OPT_ARG_FLOAT, &s->ms_search, NULL}, + {"speed", OPT_ARG_STR, &speed, NULL}, + {NULL}, + }; + if (subopt_parse(arg, subopts) != 0) { + return AF_ERROR; + } + if (s->scale_nominal <= 0) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[scaletempo] %s: %s: scale > 0\n", + mp_gtext("error parsing command line"), + mp_gtext("value out of range")); + return AF_ERROR; + } + if (s->ms_stride <= 0) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[scaletempo] %s: %s: stride > 0\n", + mp_gtext("error parsing command line"), + mp_gtext("value out of range")); + return AF_ERROR; + } + if (s->percent_overlap < 0 || s->percent_overlap > 1) { + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[scaletempo] %s: %s: 0 <= overlap <= 1\n", + mp_gtext("error parsing command line"), + mp_gtext("value out of range")); + return AF_ERROR; + } + if (s->ms_search < 0) { + mp_msg(MSGT_AFILTER, MSGL_ERR, "[scaletempo] %s: %s: search >= 0\n", + mp_gtext("error parsing command line"), + mp_gtext("value out of range")); + return AF_ERROR; + } + if (speed.len > 0) { + if (strcmp(speed.str, "pitch") == 0) { + s->speed_tempo = 0; + s->speed_pitch = 1; + } else if (strcmp(speed.str, "tempo") == 0) { + s->speed_tempo = 1; + s->speed_pitch = 0; + } else if (strcmp(speed.str, "none") == 0) { + s->speed_tempo = 0; + s->speed_pitch = 0; + } else if (strcmp(speed.str, "both") == 0) { + s->speed_tempo = 1; + s->speed_pitch = 1; + } else { + mp_msg(MSGT_AFILTER, MSGL_ERR, + "[scaletempo] %s: %s: speed=[pitch|tempo|none|both]\n", + mp_gtext("error parsing command line"), + mp_gtext("value out of range")); + return AF_ERROR; + } + } + s->scale = s->speed * s->scale_nominal; + mp_msg(MSGT_AFILTER, MSGL_DBG2, "[scaletempo] %6.3f scale, %6.2f stride, %6.2f overlap, %6.2f search, speed = %s\n", s->scale_nominal, s->ms_stride, s->percent_overlap, s->ms_search, (s->speed_tempo?(s->speed_pitch?"tempo and speed":"tempo"):(s->speed_pitch?"pitch":"none"))); + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + af_scaletempo_t* s = af->setup; + free(af->data->audio); + free(af->data); + free(s->buf_queue); + free(s->buf_overlap); + free(s->buf_pre_corr); + free(s->table_blend); + free(s->table_window); + free(af->setup); +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af_scaletempo_t* s; + + af->control = control; + af->uninit = uninit; + af->play = play; + af->mul = 1; + af->data = calloc(1,sizeof(struct mp_audio)); + af->setup = calloc(1,sizeof(af_scaletempo_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + + s = af->setup; + s->scale = s->speed = s->scale_nominal = 1.0; + s->speed_tempo = 1; + s->speed_pitch = 0; + s->ms_stride = 60; + s->percent_overlap = .20; + s->ms_search = 14; + + return AF_OK; +} + +// Description of this filter +struct af_info af_info_scaletempo = { + "Scale audio tempo while maintaining pitch", + "scaletempo", + "Robert Juliano", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_sinesuppress.c b/audio/filter/af_sinesuppress.c new file mode 100644 index 0000000000..36f7189f00 --- /dev/null +++ b/audio/filter/af_sinesuppress.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2006 Michael Niedermayer + * Copyright (C) 2004 Alex Beregszaszi + * based upon af_extrastereo.c by Pierre Lombard + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <inttypes.h> +#include <math.h> +#include <limits.h> + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_sinesuppress_s +{ + double freq; + double decay; + double real; + double imag; + double ref; + double pos; +}af_sinesuppress_t; + +static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data); +//static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data); + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_sinesuppress_t* s = (af_sinesuppress_t*)af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT:{ + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = 1; +#if 0 + if (((struct mp_audio*)arg)->format == AF_FORMAT_FLOAT_NE) + { + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + af->play = play_float; + }// else +#endif + { + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + af->play = play_s16; + } + + return af_test_output(af,(struct mp_audio*)arg); + } + case AF_CONTROL_COMMAND_LINE:{ + float f1,f2; + sscanf((char*)arg,"%f:%f", &f1,&f2); + s->freq = f1; + s->decay = f2; + return AF_OK; + } + case AF_CONTROL_SS_FREQ | AF_CONTROL_SET: + s->freq = *(float*)arg; + return AF_OK; + case AF_CONTROL_SS_FREQ | AF_CONTROL_GET: + *(float*)arg = s->freq; + return AF_OK; + case AF_CONTROL_SS_DECAY | AF_CONTROL_SET: + s->decay = *(float*)arg; + return AF_OK; + case AF_CONTROL_SS_DECAY | AF_CONTROL_GET: + *(float*)arg = s->decay; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play_s16(struct af_instance* af, struct mp_audio* data) +{ + af_sinesuppress_t *s = af->setup; + register int i = 0; + int16_t *a = (int16_t*)data->audio; // Audio data + int len = data->len/2; // Number of samples + + for (i = 0; i < len; i++) + { + double co= cos(s->pos); + double si= sin(s->pos); + + s->real += co * a[i]; + s->imag += si * a[i]; + s->ref += co * co; + + a[i] -= (s->real * co + s->imag * si) / s->ref; + + s->real -= s->real * s->decay; + s->imag -= s->imag * s->decay; + s->ref -= s->ref * s->decay; + + s->pos += 2 * M_PI * s->freq / data->rate; + } + + mp_msg(MSGT_AFILTER, MSGL_V, "[sinesuppress] f:%8.2f: amp:%8.2f\n", s->freq, sqrt(s->real*s->real + s->imag*s->imag) / s->ref); + + return data; +} + +#if 0 +static struct mp_audio* play_float(struct af_instance* af, struct mp_audio* data) +{ + af_sinesuppress_t *s = af->setup; + register int i = 0; + float *a = (float*)data->audio; // Audio data + int len = data->len/4; // Number of samples + float avg, l, r; + + for (i = 0; i < len; i+=2) + { + avg = (a[i] + a[i + 1]) / 2; + +/* l = avg + (s->mul * (a[i] - avg)); + r = avg + (s->mul * (a[i + 1] - avg));*/ + + a[i] = af_softclip(l); + a[i + 1] = af_softclip(r); + } + + return data; +} +#endif + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play_s16; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_sinesuppress_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + + ((af_sinesuppress_t*)af->setup)->freq = 50.0; + ((af_sinesuppress_t*)af->setup)->decay = 0.0001; + return AF_OK; +} + +// Description of this filter +struct af_info af_info_sinesuppress = { + "Sine Suppress", + "sinesuppress", + "Michael Niedermayer", + "", + 0, + af_open +}; diff --git a/audio/filter/af_sub.c b/audio/filter/af_sub.c new file mode 100644 index 0000000000..4af28d9141 --- /dev/null +++ b/audio/filter/af_sub.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@watri.uwa.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* This filter adds a sub-woofer channels to the audio stream by + averaging the left and right channel and low-pass filter them. The + low-pass filter is implemented as a 4th order IIR Butterworth + filter, with a variable cutoff frequency between 10 and 300 Hz. The + filter gives 24dB/octave attenuation. There are two runtime + controls one for setting which channel to insert the sub-audio into + called AF_CONTROL_SUB_CH and one for setting the cutoff frequency + called AF_CONTROL_SUB_FC. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "af.h" +#include "dsp.h" + +// Q value for low-pass filter +#define Q 1.0 + +// Analog domain biquad section +typedef struct{ + float a[3]; // Numerator coefficients + float b[3]; // Denominator coefficients +} biquad_t; + +// S-parameters for designing 4th order Butterworth filter +static biquad_t sp[2] = {{{1.0,0.0,0.0},{1.0,0.765367,1.0}}, + {{1.0,0.0,0.0},{1.0,1.847759,1.0}}}; + +// Data for specific instances of this filter +typedef struct af_sub_s +{ + float w[2][4]; // Filter taps for low-pass filter + float q[2][2]; // Circular queues + float fc; // Cutoff frequency [Hz] for low-pass filter + float k; // Filter gain; + int ch; // Channel number which to insert the filtered data + +}af_sub_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_sub_t* s = af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT:{ + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = max(s->ch+1,((struct mp_audio*)arg)->nch); + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + + // Design low-pass filter + s->k = 1.0; + if((-1 == af_filter_szxform(sp[0].a, sp[0].b, Q, s->fc, + (float)af->data->rate, &s->k, s->w[0])) || + (-1 == af_filter_szxform(sp[1].a, sp[1].b, Q, s->fc, + (float)af->data->rate, &s->k, s->w[1]))) + return AF_ERROR; + return af_test_output(af,(struct mp_audio*)arg); + } + case AF_CONTROL_COMMAND_LINE:{ + int ch=5; + float fc=60.0; + sscanf(arg,"%f:%i", &fc , &ch); + if(AF_OK != control(af,AF_CONTROL_SUB_CH | AF_CONTROL_SET, &ch)) + return AF_ERROR; + return control(af,AF_CONTROL_SUB_FC | AF_CONTROL_SET, &fc); + } + case AF_CONTROL_SUB_CH | AF_CONTROL_SET: // Requires reinit + // Sanity check + if((*(int*)arg >= AF_NCH) || (*(int*)arg < 0)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Subwoofer channel number must be between " + " 0 and %i current value is %i\n", AF_NCH-1, *(int*)arg); + return AF_ERROR; + } + s->ch = *(int*)arg; + return AF_OK; + case AF_CONTROL_SUB_CH | AF_CONTROL_GET: + *(int*)arg = s->ch; + return AF_OK; + case AF_CONTROL_SUB_FC | AF_CONTROL_SET: // Requires reinit + // Sanity check + if((*(float*)arg > 300) || (*(float*)arg < 20)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[sub] Cutoff frequency must be between 20Hz and" + " 300Hz current value is %0.2f",*(float*)arg); + return AF_ERROR; + } + // Set cutoff frequency + s->fc = *(float*)arg; + return AF_OK; + case AF_CONTROL_SUB_FC | AF_CONTROL_GET: + *(float*)arg = s->fc; + return AF_OK; + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +#ifndef IIR +#define IIR(in,w,q,out) { \ + float h0 = (q)[0]; \ + float h1 = (q)[1]; \ + float hn = (in) - h0 * (w)[0] - h1 * (w)[1]; \ + out = hn + h0 * (w)[2] + h1 * (w)[3]; \ + (q)[1] = h0; \ + (q)[0] = hn; \ +} +#endif + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + af_sub_t* s = af->setup; // Setup for this instance + float* a = c->audio; // Audio data + int len = c->len/4; // Number of samples in current audio block + int nch = c->nch; // Number of channels + int ch = s->ch; // Channel in which to insert the sub audio + register int i; + + // Run filter + for(i=0;i<len;i+=nch){ + // Average left and right + register float x = 0.5 * (a[i] + a[i+1]); + IIR(x * s->k, s->w[0], s->q[0], x); + IIR(x , s->w[1], s->q[1], a[i+ch]); + } + + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + af_sub_t* s; + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=s=calloc(1,sizeof(af_sub_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + // Set default values + s->ch = 5; // Channel nr 6 + s->fc = 60; // Cutoff frequency 60Hz + return AF_OK; +} + +// Description of this filter +struct af_info af_info_sub = { + "Audio filter for adding a sub-base channel", + "sub", + "Anders", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_surround.c b/audio/filter/af_surround.c new file mode 100644 index 0000000000..57288d6ba2 --- /dev/null +++ b/audio/filter/af_surround.c @@ -0,0 +1,273 @@ +/* + * Filter to do simple decoding of matrixed surround sound. + * This will provide a (basic) surround-sound effect from + * audio encoded for Dolby Surround, Pro Logic etc. + * + * original author: Steve Davies <steve@daviesfam.org> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* The principle: Make rear channels by extracting anti-phase data + from the front channels, delay by 20ms and feed to rear in anti-phase +*/ + + +/* SPLITREAR: Define to decode two distinct rear channels - this + doesn't work so well in practice because separation in a passive + matrix is not high. C (dialogue) to Ls and Rs 14dB or so - so + dialogue leaks to the rear. Still - give it a try and send + feedback. Comment this define for old behavior of a single + surround sent to rear in anti-phase */ +#define SPLITREAR 1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "af.h" +#include "dsp.h" + +#define L 32 // Length of fir filter +#define LD 65536 // Length of delay buffer + +// 32 Tap fir filter loop unrolled +#define FIR(x,w,y) \ + y = ( w[0] *x[0] +w[1] *x[1] +w[2] *x[2] +w[3] *x[3] \ + + w[4] *x[4] +w[5] *x[5] +w[6] *x[6] +w[7] *x[7] \ + + w[8] *x[8] +w[9] *x[9] +w[10]*x[10]+w[11]*x[11] \ + + w[12]*x[12]+w[13]*x[13]+w[14]*x[14]+w[15]*x[15] \ + + w[16]*x[16]+w[17]*x[17]+w[18]*x[18]+w[19]*x[19] \ + + w[20]*x[20]+w[21]*x[21]+w[22]*x[22]+w[23]*x[23] \ + + w[24]*x[24]+w[25]*x[25]+w[26]*x[26]+w[27]*x[27] \ + + w[28]*x[28]+w[29]*x[29]+w[30]*x[30]+w[31]*x[31]) + +// Add to circular queue macro + update index +#ifdef SPLITREAR +#define ADDQUE(qi,rq,lq,r,l)\ + lq[qi]=lq[qi+L]=(l);\ + rq[qi]=rq[qi+L]=(r);\ + qi=(qi-1)&(L-1); +#else +#define ADDQUE(qi,lq,l)\ + lq[qi]=lq[qi+L]=(l);\ + qi=(qi-1)&(L-1); +#endif + +// Macro for updating queue index in delay queues +#define UPDATEQI(qi) qi=(qi+1)&(LD-1) + +// instance data +typedef struct af_surround_s +{ + float lq[2*L]; // Circular queue for filtering left rear channel + float rq[2*L]; // Circular queue for filtering right rear channel + float w[L]; // FIR filter coefficients for surround sound 7kHz low-pass + float* dr; // Delay queue right rear channel + float* dl; // Delay queue left rear channel + float d; // Delay time + int i; // Position in circular buffer + int wi; // Write index for delay queue + int ri; // Read index for delay queue +}af_surround_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_surround_t *s = af->setup; + switch(cmd){ + case AF_CONTROL_REINIT:{ + float fc; + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch*2; + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + + if (af->data->nch != 4){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Only stereo input is supported.\n"); + return AF_DETACH; + } + // Surround filer coefficients + fc = 2.0 * 7000.0/(float)af->data->rate; + if (-1 == af_filter_design_fir(L, s->w, &fc, LP|HAMMING, 0)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Unable to design low-pass filter.\n"); + return AF_ERROR; + } + + // Free previous delay queues + free(s->dl); + free(s->dr); + // Allocate new delay queues + s->dl = calloc(LD,af->data->bps); + s->dr = calloc(LD,af->data->bps); + if((NULL == s->dl) || (NULL == s->dr)) + mp_msg(MSGT_AFILTER, MSGL_FATAL, "[delay] Out of memory\n"); + + // Initialize delay queue index + if(AF_OK != af_from_ms(1, &s->d, &s->wi, af->data->rate, 0.0, 1000.0)) + return AF_ERROR; +// printf("%i\n",s->wi); + s->ri = 0; + + if((af->data->format != ((struct mp_audio*)arg)->format) || + (af->data->bps != ((struct mp_audio*)arg)->bps)){ + ((struct mp_audio*)arg)->format = af->data->format; + ((struct mp_audio*)arg)->bps = af->data->bps; + return AF_FALSE; + } + return AF_OK; + } + case AF_CONTROL_COMMAND_LINE:{ + float d = 0; + sscanf((char*)arg,"%f",&d); + if ((d < 0) || (d > 1000)){ + mp_msg(MSGT_AFILTER, MSGL_ERR, "[surround] Invalid delay time, valid time values" + " are 0ms to 1000ms current value is %0.3f ms\n",d); + return AF_ERROR; + } + s->d = d; + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + if(af->data) + free(af->data->audio); + free(af->data); + free(af->setup); +} + +// The beginnings of an active matrix... +static float steering_matrix[][12] = { +// LL RL LR RR LS RS +// LLs RLs LRs RRs LC RC + {.707, .0, .0, .707, .5, -.5, + .5878, -.3928, .3928, -.5878, .5, .5}, +}; + +// Experimental moving average dominance +//static int amp_L = 0, amp_R = 0, amp_C = 0, amp_S = 0; + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data){ + af_surround_t* s = (af_surround_t*)af->setup; + float* m = steering_matrix[0]; + float* in = data->audio; // Input audio data + float* out = NULL; // Output audio data + float* end = in + data->len / sizeof(float); // Loop end + int i = s->i; // Filter queue index + int ri = s->ri; // Read index for delay queue + int wi = s->wi; // Write index for delay queue + + if (AF_OK != RESIZE_LOCAL_BUFFER(af, data)) + return NULL; + + out = af->data->audio; + + while(in < end){ + /* Dominance: + abs(in[0]) abs(in[1]); + abs(in[0]+in[1]) abs(in[0]-in[1]); + 10 * log( abs(in[0]) / (abs(in[1])|1) ); + 10 * log( abs(in[0]+in[1]) / (abs(in[0]-in[1])|1) ); */ + + /* About volume balancing... + Surround encoding does the following: + Lt=L+.707*C+.707*S, Rt=R+.707*C-.707*S + So S should be extracted as: + (Lt-Rt) + But we are splitting the S to two output channels, so we + must take 3dB off as we split it: + Ls=Rs=.707*(Lt-Rt) + Trouble is, Lt could be +1, Rt -1, so possibility that S will + overflow. So to avoid that, we cut L/R by 3dB (*.707), and S by + 6dB (/2). This keeps the overall balance, but guarantees no + overflow. */ + + // Output front left and right + out[0] = m[0]*in[0] + m[1]*in[1]; + out[1] = m[2]*in[0] + m[3]*in[1]; + + // Low-pass output @ 7kHz + FIR((&s->lq[i]), s->w, s->dl[wi]); + + // Delay output by d ms + out[2] = s->dl[ri]; + +#ifdef SPLITREAR + // Low-pass output @ 7kHz + FIR((&s->rq[i]), s->w, s->dr[wi]); + + // Delay output by d ms + out[3] = s->dr[ri]; +#else + out[3] = -out[2]; +#endif + + // Update delay queues indexes + UPDATEQI(ri); + UPDATEQI(wi); + + // Calculate and save surround in circular queue +#ifdef SPLITREAR + ADDQUE(i, s->rq, s->lq, m[6]*in[0]+m[7]*in[1], m[8]*in[0]+m[9]*in[1]); +#else + ADDQUE(i, s->lq, m[4]*in[0]+m[5]*in[1]); +#endif + + // Next sample... + in = &in[data->nch]; + out = &out[af->data->nch]; + } + + // Save indexes + s->i = i; s->ri = ri; s->wi = wi; + + // Set output data + data->audio = af->data->audio; + data->len *= 2; + data->nch = af->data->nch; + + return data; +} + +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=2; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_surround_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + ((af_surround_t*)af->setup)->d = 20; + return AF_OK; +} + +struct af_info af_info_surround = +{ + "Surround decoder filter", + "surround", + "Steve Davies <steve@daviesfam.org>", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_sweep.c b/audio/filter/af_sweep.c new file mode 100644 index 0000000000..6d1106fefc --- /dev/null +++ b/audio/filter/af_sweep.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include <math.h> + +#include "config.h" +#include "af.h" + +typedef struct af_sweep_s{ + double x; + double delta; +}af_sweept; + + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_sweept* s = (af_sweept*)af->setup; + struct mp_audio *data= (struct mp_audio*)arg; + + switch(cmd){ + case AF_CONTROL_REINIT: + af->data->nch = data->nch; + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + af->data->rate = data->rate; + + return AF_OK; + case AF_CONTROL_COMMAND_LINE: + sscanf((char*)arg,"%lf", &s->delta); + return AF_OK; +/* case AF_CONTROL_RESAMPLE_RATE | AF_CONTROL_SET: + af->data->rate = *(int*)arg; + return AF_OK;*/ + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + af_sweept *s = af->setup; + int i, j; + int16_t *in = (int16_t*)data->audio; + int chans = data->nch; + int in_len = data->len/(2*chans); + + for(i=0; i<in_len; i++){ + for(j=0; j<chans; j++) + in[i*chans+j]= 32000*sin(s->x*s->x); + s->x += s->delta; + if(2*s->x*s->delta >= 3.141592) s->x=0; + } + + return data; +} + +static int af_open(struct af_instance* af){ + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_sweept)); + return AF_OK; +} + +struct af_info af_info_sweep = { + "sine sweep", + "sweep", + "Michael Niedermayer", + "", + AF_FLAGS_REENTRANT, + af_open +}; diff --git a/audio/filter/af_tools.c b/audio/filter/af_tools.c new file mode 100644 index 0000000000..0d5dc6c573 --- /dev/null +++ b/audio/filter/af_tools.c @@ -0,0 +1,110 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <math.h> +#include <string.h> +#include "af.h" + +/* Convert to gain value from dB. Returns AF_OK if of and AF_ERROR if + fail */ +int af_from_dB(int n, float* in, float* out, float k, float mi, float ma) +{ + int i = 0; + // Sanity check + if(!in || !out) + return AF_ERROR; + + for(i=0;i<n;i++){ + if(in[i]<=-200) + out[i]=0.0; + else + out[i]=pow(10.0,clamp(in[i],mi,ma)/k); + } + return AF_OK; +} + +/* Convert from gain value to dB. Returns AF_OK if of and AF_ERROR if + fail */ +int af_to_dB(int n, float* in, float* out, float k) +{ + int i = 0; + // Sanity check + if(!in || !out) + return AF_ERROR; + + for(i=0;i<n;i++){ + if(in[i] == 0.0) + out[i]=-200.0; + else + out[i]=k*log10(in[i]); + } + return AF_OK; +} + +/* Convert from ms to sample time */ +int af_from_ms(int n, float* in, int* out, int rate, float mi, float ma) +{ + int i = 0; + // Sanity check + if(!in || !out) + return AF_ERROR; + + for(i=0;i<n;i++) + out[i]=(int)((float)rate * clamp(in[i],mi,ma)/1000.0); + + return AF_OK; +} + +/* Convert from sample time to ms */ +int af_to_ms(int n, int* in, float* out, int rate) +{ + int i = 0; + // Sanity check + if(!in || !out || !rate) + return AF_ERROR; + + for(i=0;i<n;i++) + out[i]=1000.0 * (float)in[i]/((float)rate); + + return AF_OK; +} + +/* Helper function for testing the output format */ +int af_test_output(struct af_instance* af, struct mp_audio* out) +{ + if((af->data->format != out->format) || + (af->data->bps != out->bps) || + (af->data->rate != out->rate) || + (af->data->nch != out->nch)){ + memcpy(out,af->data,sizeof(struct mp_audio)); + return AF_FALSE; + } + return AF_OK; +} + +/* Soft clipping, the sound of a dream, thanks to Jon Wattes + post to Musicdsp.org */ +float af_softclip(float a) +{ + if (a >= M_PI/2) + return 1.0; + else if (a <= -M_PI/2) + return -1.0; + else + return sin(a); +} diff --git a/audio/filter/af_volnorm.c b/audio/filter/af_volnorm.c new file mode 100644 index 0000000000..b4c204d305 --- /dev/null +++ b/audio/filter/af_volnorm.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2004 Alex Beregszaszi & Pierre Lombard + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <inttypes.h> +#include <math.h> +#include <limits.h> + +#include "af.h" + +// Methods: +// 1: uses a 1 value memory and coefficients new=a*old+b*cur (with a+b=1) +// 2: uses several samples to smooth the variations (standard weighted mean +// on past samples) + +// Size of the memory array +// FIXME: should depend on the frequency of the data (should be a few seconds) +#define NSAMPLES 128 + +// If summing all the mem[].len is lower than MIN_SAMPLE_SIZE bytes, then we +// choose to ignore the computed value as it's not significant enough +// FIXME: should depend on the frequency of the data (0.5s maybe) +#define MIN_SAMPLE_SIZE 32000 + +// mul is the value by which the samples are scaled +// and has to be in [MUL_MIN, MUL_MAX] +#define MUL_INIT 1.0 +#define MUL_MIN 0.1 +#define MUL_MAX 5.0 + +// Silence level +// FIXME: should be relative to the level of the samples +#define SIL_S16 (SHRT_MAX * 0.01) +#define SIL_FLOAT (INT_MAX * 0.01) // FIXME + +// smooth must be in ]0.0, 1.0[ +#define SMOOTH_MUL 0.06 +#define SMOOTH_LASTAVG 0.06 + +#define DEFAULT_TARGET 0.25 + +// Data for specific instances of this filter +typedef struct af_volume_s +{ + int method; // method used + float mul; + // method 1 + float lastavg; // history value of the filter + // method 2 + int idx; + struct { + float avg; // average level of the sample + int len; // sample size (weight) + } mem[NSAMPLES]; + // "Ideal" level + float mid_s16; + float mid_float; +}af_volnorm_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_volnorm_t* s = (af_volnorm_t*)af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT: + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + + if(((struct mp_audio*)arg)->format == (AF_FORMAT_S16_NE)){ + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + }else{ + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + } + return af_test_output(af,(struct mp_audio*)arg); + case AF_CONTROL_COMMAND_LINE:{ + int i = 0; + float target = DEFAULT_TARGET; + sscanf((char*)arg,"%d:%f", &i, &target); + if (i != 1 && i != 2) + return AF_ERROR; + s->method = i-1; + s->mid_s16 = ((float)SHRT_MAX) * target; + s->mid_float = ((float)INT_MAX) * target; + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +static void method1_int16(af_volnorm_t *s, struct mp_audio *c) +{ + register int i = 0; + int16_t *data = (int16_t*)c->audio; // Audio data + int len = c->len/2; // Number of samples + float curavg = 0.0, newavg, neededmul; + int tmp; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + + if (curavg > SIL_S16) + { + neededmul = s->mid_s16 / (curavg * s->mul); + s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul; + + // clamp the mul coefficient + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + { + tmp = s->mul * data[i]; + tmp = clamp(tmp, SHRT_MIN, SHRT_MAX); + data[i] = tmp; + } + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg; +} + +static void method1_float(af_volnorm_t *s, struct mp_audio *c) +{ + register int i = 0; + float *data = (float*)c->audio; // Audio data + int len = c->len/4; // Number of samples + float curavg = 0.0, newavg, neededmul, tmp; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + + if (curavg > SIL_FLOAT) // FIXME + { + neededmul = s->mid_float / (curavg * s->mul); + s->mul = (1.0 - SMOOTH_MUL) * s->mul + SMOOTH_MUL * neededmul; + + // clamp the mul coefficient + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + data[i] *= s->mul; + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->lastavg = (1.0 - SMOOTH_LASTAVG) * s->lastavg + SMOOTH_LASTAVG * newavg; +} + +static void method2_int16(af_volnorm_t *s, struct mp_audio *c) +{ + register int i = 0; + int16_t *data = (int16_t*)c->audio; // Audio data + int len = c->len/2; // Number of samples + float curavg = 0.0, newavg, avg = 0.0; + int tmp, totallen = 0; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + for (i = 0; i < NSAMPLES; i++) + { + avg += s->mem[i].avg * (float)s->mem[i].len; + totallen += s->mem[i].len; + } + + if (totallen > MIN_SAMPLE_SIZE) + { + avg /= (float)totallen; + if (avg >= SIL_S16) + { + s->mul = s->mid_s16 / avg; + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + { + tmp = s->mul * data[i]; + tmp = clamp(tmp, SHRT_MIN, SHRT_MAX); + data[i] = tmp; + } + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->mem[s->idx].len = len; + s->mem[s->idx].avg = newavg; + s->idx = (s->idx + 1) % NSAMPLES; +} + +static void method2_float(af_volnorm_t *s, struct mp_audio *c) +{ + register int i = 0; + float *data = (float*)c->audio; // Audio data + int len = c->len/4; // Number of samples + float curavg = 0.0, newavg, avg = 0.0, tmp; + int totallen = 0; + + for (i = 0; i < len; i++) + { + tmp = data[i]; + curavg += tmp * tmp; + } + curavg = sqrt(curavg / (float) len); + + // Evaluate an adequate 'mul' coefficient based on previous state, current + // samples level, etc + for (i = 0; i < NSAMPLES; i++) + { + avg += s->mem[i].avg * (float)s->mem[i].len; + totallen += s->mem[i].len; + } + + if (totallen > MIN_SAMPLE_SIZE) + { + avg /= (float)totallen; + if (avg >= SIL_FLOAT) + { + s->mul = s->mid_float / avg; + s->mul = clamp(s->mul, MUL_MIN, MUL_MAX); + } + } + + // Scale & clamp the samples + for (i = 0; i < len; i++) + data[i] *= s->mul; + + // Evaulation of newavg (not 100% accurate because of values clamping) + newavg = s->mul * curavg; + + // Stores computed values for future smoothing + s->mem[s->idx].len = len; + s->mem[s->idx].avg = newavg; + s->idx = (s->idx + 1) % NSAMPLES; +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + af_volnorm_t *s = af->setup; + + if(af->data->format == (AF_FORMAT_S16_NE)) + { + if (s->method) + method2_int16(s, data); + else + method1_int16(s, data); + } + else if(af->data->format == (AF_FORMAT_FLOAT_NE)) + { + if (s->method) + method2_float(s, data); + else + method1_float(s, data); + } + return data; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + int i = 0; + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_volnorm_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + + ((af_volnorm_t*)af->setup)->mul = MUL_INIT; + ((af_volnorm_t*)af->setup)->lastavg = ((float)SHRT_MAX) * DEFAULT_TARGET; + ((af_volnorm_t*)af->setup)->idx = 0; + ((af_volnorm_t*)af->setup)->mid_s16 = ((float)SHRT_MAX) * DEFAULT_TARGET; + ((af_volnorm_t*)af->setup)->mid_float = ((float)INT_MAX) * DEFAULT_TARGET; + for (i = 0; i < NSAMPLES; i++) + { + ((af_volnorm_t*)af->setup)->mem[i].len = 0; + ((af_volnorm_t*)af->setup)->mem[i].avg = 0; + } + return AF_OK; +} + +// Description of this filter +struct af_info af_info_volnorm = { + "Volume normalizer filter", + "volnorm", + "Alex Beregszaszi & Pierre Lombard", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c new file mode 100644 index 0000000000..ecf181c8b8 --- /dev/null +++ b/audio/filter/af_volume.c @@ -0,0 +1,226 @@ +/* + * Copyright (C)2002 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* This audio filter changes the volume of the sound, and can be used + when the mixer doesn't support the PCM channel. It can handle + between 1 and AF_NCH channels. The volume can be adjusted between -60dB + to +20dB and is set on a per channels basis. The is accessed through + AF_CONTROL_VOLUME_LEVEL. + + The filter has support for soft-clipping, it is enabled by + AF_CONTROL_VOLUME_SOFTCLIPP. It has also a probing feature which + can be used to measure the power in the audio stream, both an + instantaneous value and the maximum value can be probed. The + probing is enable by AF_CONTROL_VOLUME_PROBE_ON_OFF and is done on a + per channel basis. The result from the probing is obtained using + AF_CONTROL_VOLUME_PROBE_GET and AF_CONTROL_VOLUME_PROBE_GET_MAX. The + probed values are calculated in dB. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <inttypes.h> +#include <math.h> +#include <limits.h> + +#include "af.h" + +// Data for specific instances of this filter +typedef struct af_volume_s +{ + int enable[AF_NCH]; // Enable/disable / channel + float pow[AF_NCH]; // Estimated power level [dB] + float max[AF_NCH]; // Max Power level [dB] + float level[AF_NCH]; // Gain level for each channel + float time; // Forgetting factor for power estimate + int soft; // Enable/disable soft clipping + int fast; // Use fix-point volume control +}af_volume_t; + +// Initialization and runtime control +static int control(struct af_instance* af, int cmd, void* arg) +{ + af_volume_t* s = (af_volume_t*)af->setup; + + switch(cmd){ + case AF_CONTROL_REINIT: + // Sanity check + if(!arg) return AF_ERROR; + + af->data->rate = ((struct mp_audio*)arg)->rate; + af->data->nch = ((struct mp_audio*)arg)->nch; + + if(s->fast && (((struct mp_audio*)arg)->format != (AF_FORMAT_FLOAT_NE))){ + af->data->format = AF_FORMAT_S16_NE; + af->data->bps = 2; + } + else{ + // Cutoff set to 10Hz for forgetting factor + float x = 2.0*M_PI*15.0/(float)af->data->rate; + float t = 2.0-cos(x); + s->time = 1.0 - (t - sqrt(t*t - 1)); + mp_msg(MSGT_AFILTER, MSGL_DBG2, "[volume] Forgetting factor = %0.5f\n",s->time); + af->data->format = AF_FORMAT_FLOAT_NE; + af->data->bps = 4; + } + return af_test_output(af,(struct mp_audio*)arg); + case AF_CONTROL_COMMAND_LINE:{ + float v=0.0; + float vol[AF_NCH]; + int i; + sscanf((char*)arg,"%f:%i", &v, &s->soft); + for(i=0;i<AF_NCH;i++) vol[i]=v; + return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol); + } + case AF_CONTROL_POST_CREATE: + s->fast = ((((struct af_cfg*)arg)->force & AF_INIT_FORMAT_MASK) == + AF_INIT_FLOAT) ? 0 : 1; + return AF_OK; + case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_SET: + memcpy(s->enable,(int*)arg,AF_NCH*sizeof(int)); + return AF_OK; + case AF_CONTROL_VOLUME_ON_OFF | AF_CONTROL_GET: + memcpy((int*)arg,s->enable,AF_NCH*sizeof(int)); + return AF_OK; + case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_SET: + s->soft = *(int*)arg; + return AF_OK; + case AF_CONTROL_VOLUME_SOFTCLIP | AF_CONTROL_GET: + *(int*)arg = s->soft; + return AF_OK; + case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET: + return af_from_dB(AF_NCH,(float*)arg,s->level,20.0,-200.0,60.0); + case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET: + return af_to_dB(AF_NCH,s->level,(float*)arg,20.0); + case AF_CONTROL_VOLUME_PROBE | AF_CONTROL_GET: + return af_to_dB(AF_NCH,s->pow,(float*)arg,10.0); + case AF_CONTROL_VOLUME_PROBE_MAX | AF_CONTROL_GET: + return af_to_dB(AF_NCH,s->max,(float*)arg,10.0); + case AF_CONTROL_PRE_DESTROY:{ + float m = 0.0; + int i; + if(!s->fast){ + for(i=0;i<AF_NCH;i++) + m=max(m,s->max[i]); + af_to_dB(1, &m, &m, 10.0); + mp_msg(MSGT_AFILTER, MSGL_INFO, "[volume] The maximum volume was %0.2fdB \n", m); + } + return AF_OK; + } + } + return AF_UNKNOWN; +} + +// Deallocate memory +static void uninit(struct af_instance* af) +{ + free(af->data); + free(af->setup); +} + +// Filter data through filter +static struct mp_audio* play(struct af_instance* af, struct mp_audio* data) +{ + struct mp_audio* c = data; // Current working data + af_volume_t* s = (af_volume_t*)af->setup; // Setup for this instance + register int nch = c->nch; // Number of channels + register int i = 0; + + // Basic operation volume control only (used on slow machines) + if(af->data->format == (AF_FORMAT_S16_NE)){ + int16_t* a = (int16_t*)c->audio; // Audio data + int len = c->len/2; // Number of samples + for (int ch = 0; ch < nch; ch++) { + int vol = 256.0 * s->level[ch]; + if (s->enable[ch] && vol != 256) { + for(i=ch;i<len;i+=nch){ + register int x = (a[i] * vol) >> 8; + a[i]=clamp(x,SHRT_MIN,SHRT_MAX); + } + } + } + } + // Machine is fast and data is floating point + else if(af->data->format == (AF_FORMAT_FLOAT_NE)){ + float* a = (float*)c->audio; // Audio data + int len = c->len/4; // Number of samples + for (int ch = 0; ch < nch; ch++) { + // Volume control (fader) + if(s->enable[ch]){ + float t = 1.0 - s->time; + for(i=ch;i<len;i+=nch){ + register float x = a[i]; + register float pow = x*x; + // Check maximum power value + if(pow > s->max[ch]) + s->max[ch] = pow; + // Set volume + x *= s->level[ch]; + // Peak meter + pow = x*x; + if(pow > s->pow[ch]) + s->pow[ch] = pow; + else + s->pow[ch] = t*s->pow[ch] + pow*s->time; // LP filter + /* Soft clipping, the sound of a dream, thanks to Jon Wattes + post to Musicdsp.org */ + if(s->soft) + x=af_softclip(x); + // Hard clipping + else + x=clamp(x,-1.0,1.0); + a[i] = x; + } + } + } + } + return c; +} + +// Allocate memory and set function pointers +static int af_open(struct af_instance* af){ + int i = 0; + af->control=control; + af->uninit=uninit; + af->play=play; + af->mul=1; + af->data=calloc(1,sizeof(struct mp_audio)); + af->setup=calloc(1,sizeof(af_volume_t)); + if(af->data == NULL || af->setup == NULL) + return AF_ERROR; + // Enable volume control and set initial volume to 0dB. + for(i=0;i<AF_NCH;i++){ + ((af_volume_t*)af->setup)->enable[i] = 1; + ((af_volume_t*)af->setup)->level[i] = 1.0; + } + return AF_OK; +} + +// Description of this filter +struct af_info af_info_volume = { + "Volume control audio filter", + "volume", + "Anders", + "", + AF_FLAGS_NOT_REENTRANT, + af_open +}; diff --git a/audio/filter/control.h b/audio/filter/control.h new file mode 100644 index 0000000000..323b9a3924 --- /dev/null +++ b/audio/filter/control.h @@ -0,0 +1,257 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_CONTROL_H +#define MPLAYER_CONTROL_H + +#include <sys/types.h> + +/********************************************* +// Control info struct. +// +// This struct is the argument in a info call to a filter. +*/ + +// Argument types +#define AF_CONTROL_TYPE_BOOL (0x0<<0) +#define AF_CONTROL_TYPE_CHAR (0x1<<0) +#define AF_CONTROL_TYPE_INT (0x2<<0) +#define AF_CONTROL_TYPE_FLOAT (0x3<<0) +#define AF_CONTROL_TYPE_STRUCT (0x4<<0) +#define AF_CONTROL_TYPE_SPECIAL (0x5<<0) // a pointer to a function for example +#define AF_CONTROL_TYPE_MASK (0x7<<0) +// Argument geometry +#define AF_CONTROL_GEOM_SCALAR (0x0<<3) +#define AF_CONTROL_GEOM_ARRAY (0x1<<3) +#define AF_CONTROL_GEOM_MATRIX (0x2<<3) +#define AF_CONTROL_GEOM_MASK (0x3<<3) +// Argument properties +#define AF_CONTROL_PROP_READ (0x0<<5) // The argument can be read +#define AF_CONTROL_PROP_WRITE (0x1<<5) // The argument can be written +#define AF_CONTROL_PROP_SAVE (0x2<<5) // Can be saved +#define AF_CONTROL_PROP_RUNTIME (0x4<<5) // Acessable during execution +#define AF_CONTROL_PROP_CHANNEL (0x8<<5) // Argument is set per channel +#define AF_CONTROL_PROP_MASK (0xF<<5) + +typedef struct af_control_info_s{ + int def; // Control enumrification + char* name; // Name of argument + char* info; // Description of what it does + int flags; // Flags as defined above + float max; // Max and min value + float min; // (only aplicable on float and int) + int xdim; // 1st dimension + int ydim; // 2nd dimension (=0 for everything except matrix) + size_t sz; // Size of argument in bytes + int ch; // Channel number (for future use) + void* arg; // Data (for future use) +}af_control_info_t; + + +/********************************************* +// Extended control used with arguments that operates on only one +// channel at the time +*/ +typedef struct af_control_ext_s{ + void* arg; // Argument + int ch; // Chanel number +}af_control_ext_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 0x10000000 +#define AF_CONTROL_OPTIONAL 0x20000000 +#define AF_CONTROL_FILTER_SPECIFIC 0x40000000 + +// MANDATORY CALLS + +/* Reinitialize filter. The optional argument contains the new + configuration in form of a struct mp_audio 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 0x00000100 | AF_CONTROL_MANDATORY + +// OPTIONAL CALLS + +/* Called just after creation with the af_cfg for the stream in which + the filter resides as input parameter this call can be used by the + filter to initialize itself */ +#define AF_CONTROL_POST_CREATE 0x00000100 | AF_CONTROL_OPTIONAL + +// Called just before destruction of a filter +#define AF_CONTROL_PRE_DESTROY 0x00000200 | AF_CONTROL_OPTIONAL + +/* Commandline parameters. If there were any commandline parameters + for this specific filter, they will be given as a char* in the + argument */ +#define AF_CONTROL_COMMAND_LINE 0x00000300 | AF_CONTROL_OPTIONAL + + +// FILTER SPECIFIC CALLS + +// Basic operations: These can be ored with any of the below calls +// Set argument +#define AF_CONTROL_SET 0x00000000 +// Get argument +#define AF_CONTROL_GET 0x00000001 +// Get info about the control, i.e fill in everything except argument +#define AF_CONTROL_INFO 0x00000002 + +// Resample + +// Set output rate in resample +#define AF_CONTROL_RESAMPLE_RATE 0x00000100 | AF_CONTROL_FILTER_SPECIFIC + +// Enable sloppy resampling +#define AF_CONTROL_RESAMPLE_SLOPPY 0x00000200 | AF_CONTROL_FILTER_SPECIFIC + +// Set resampling accuracy +#define AF_CONTROL_RESAMPLE_ACCURACY 0x00000300 | AF_CONTROL_FILTER_SPECIFIC + +// Format + +#define AF_CONTROL_FORMAT_FMT 0x00000400 | AF_CONTROL_FILTER_SPECIFIC + +// Channels + +// Set number of output channels in channels +#define AF_CONTROL_CHANNELS 0x00000600 | AF_CONTROL_FILTER_SPECIFIC + +// Set number of channel routes +#define AF_CONTROL_CHANNELS_ROUTES 0x00000700 | AF_CONTROL_FILTER_SPECIFIC + +// Set channel routing pair, arg is int[2] and ch is used +#define AF_CONTROL_CHANNELS_ROUTING 0x00000800 | AF_CONTROL_FILTER_SPECIFIC + +// Set nuber of channel routing pairs, arg is int* +#define AF_CONTROL_CHANNELS_NR 0x00000900 | AF_CONTROL_FILTER_SPECIFIC + +// Set make af_channels into a router +#define AF_CONTROL_CHANNELS_ROUTER 0x00000A00 | AF_CONTROL_FILTER_SPECIFIC + +// Volume + +// Turn volume control on and off, arg is int* +#define AF_CONTROL_VOLUME_ON_OFF 0x00000B00 | AF_CONTROL_FILTER_SPECIFIC + +// Turn soft clipping of the volume on and off, arg is binary +#define AF_CONTROL_VOLUME_SOFTCLIP 0x00000C00 | AF_CONTROL_FILTER_SPECIFIC + +// Set volume level, arg is a float* with the volume for all the channels +#define AF_CONTROL_VOLUME_LEVEL 0x00000D00 | AF_CONTROL_FILTER_SPECIFIC + +// Probed power level for all channels, arg is a float* +#define AF_CONTROL_VOLUME_PROBE 0x00000E00 | AF_CONTROL_FILTER_SPECIFIC + +// Maximum probed power level for all channels, arg is a float* +#define AF_CONTROL_VOLUME_PROBE_MAX 0x00000F00 | AF_CONTROL_FILTER_SPECIFIC + +// Compressor/expander + +// Turn compressor/expander on and off +#define AF_CONTROL_COMP_ON_OFF 0x00001000 | AF_CONTROL_FILTER_SPECIFIC + +// Compression/expansion threshold [dB] +#define AF_CONTROL_COMP_THRESH 0x00001100 | AF_CONTROL_FILTER_SPECIFIC + +// Compression/expansion attack time [ms] +#define AF_CONTROL_COMP_ATTACK 0x00001200 | AF_CONTROL_FILTER_SPECIFIC + +// Compression/expansion release time [ms] +#define AF_CONTROL_COMP_RELEASE 0x00001300 | AF_CONTROL_FILTER_SPECIFIC + +// Compression/expansion gain level [dB] +#define AF_CONTROL_COMP_RATIO 0x00001400 | AF_CONTROL_FILTER_SPECIFIC + +// Noise gate + +// Turn noise gate on an off +#define AF_CONTROL_GATE_ON_OFF 0x00001500 | AF_CONTROL_FILTER_SPECIFIC + +// Noise gate threshold [dB] +#define AF_CONTROL_GATE_THRESH 0x00001600 | AF_CONTROL_FILTER_SPECIFIC + +// Noise gate attack time [ms] +#define AF_CONTROL_GATE_ATTACK 0x00001700 | AF_CONTROL_FILTER_SPECIFIC + +// Noise gate release time [ms] +#define AF_CONTROL_GATE_RELEASE 0x00001800 | AF_CONTROL_FILTER_SPECIFIC + +// Noise gate release range level [dB] +#define AF_CONTROL_GATE_RANGE 0x00001900 | AF_CONTROL_FILTER_SPECIFIC + +// Pan + +// Pan levels, arg is a control_ext with a float* +#define AF_CONTROL_PAN_LEVEL 0x00001A00 | AF_CONTROL_FILTER_SPECIFIC + +// Number of outputs from pan, arg is int* +#define AF_CONTROL_PAN_NOUT 0x00001B00 | AF_CONTROL_FILTER_SPECIFIC + +// Balance, arg is float*; range -1 (left) to 1 (right), 0 center +#define AF_CONTROL_PAN_BALANCE 0x00001C00 | AF_CONTROL_FILTER_SPECIFIC + +// Set equalizer gain, arg is a control_ext with a float* +#define AF_CONTROL_EQUALIZER_GAIN 0x00001D00 | AF_CONTROL_FILTER_SPECIFIC + + +// Delay length in ms, arg is a control_ext with a float* +#define AF_CONTROL_DELAY_LEN 0x00001E00 | AF_CONTROL_FILTER_SPECIFIC + + +// Subwoofer + +// Channel number which to insert the filtered data, arg in int* +#define AF_CONTROL_SUB_CH 0x00001F00 | AF_CONTROL_FILTER_SPECIFIC + +// Cutoff frequency [Hz] for lowpass filter, arg is float* +#define AF_CONTROL_SUB_FC 0x00002000 | AF_CONTROL_FILTER_SPECIFIC + + +// Export +#define AF_CONTROL_EXPORT_SZ 0x00003000 | AF_CONTROL_FILTER_SPECIFIC + + +// ExtraStereo Multiplier +#define AF_CONTROL_ES_MUL 0x00003100 | AF_CONTROL_FILTER_SPECIFIC + + +// Center + +// Channel number which to inster the filtered data, arg in int* +#define AF_CONTROL_CENTER_CH 0x00003200 | AF_CONTROL_FILTER_SPECIFIC + + +// SineSuppress +#define AF_CONTROL_SS_FREQ 0x00003300 | AF_CONTROL_FILTER_SPECIFIC +#define AF_CONTROL_SS_DECAY 0x00003400 | AF_CONTROL_FILTER_SPECIFIC + +#define AF_CONTROL_PLAYBACK_SPEED 0x00003500 | AF_CONTROL_FILTER_SPECIFIC +#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00003600 | AF_CONTROL_FILTER_SPECIFIC + +#endif /* MPLAYER_CONTROL_H */ diff --git a/audio/filter/dsp.h b/audio/filter/dsp.h new file mode 100644 index 0000000000..561b86cfe0 --- /dev/null +++ b/audio/filter/dsp.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_DSP_H +#define MPLAYER_DSP_H + +/* Implementation of routines used for DSP */ + +/* Size of floating point type used in routines */ +#define FLOAT_TYPE float + +#include "window.h" +#include "filter.h" + +#endif /* MPLAYER_DSP_H */ diff --git a/audio/filter/equalizer.h b/audio/filter/equalizer.h new file mode 100644 index 0000000000..4935401413 --- /dev/null +++ b/audio/filter/equalizer.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2002 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_EQUALIZER_H +#define MPLAYER_EQUALIZER_H + +/* Equalizer plugin header file defines struct used for setting or + getting the gain of a specific channel and frequency */ + +typedef struct equalizer_s +{ + float gain; // Gain in dB -15 - 15 + int channel; // Channel number 0 - 5 + int band; // Frequency band 0 - 9 +}equalizer_t; + +/* The different frequency bands are: +nr. center frequency +0 31.25 Hz +1 62.50 Hz +2 125.0 Hz +3 250.0 Hz +4 500.0 Hz +5 1.000 kHz +6 2.000 kHz +7 4.000 kHz +8 8.000 kHz +9 16.00 kHz +*/ + +#endif /* MPLAYER_EQUALIZER_H */ diff --git a/audio/filter/filter.c b/audio/filter/filter.c new file mode 100644 index 0000000000..b272125fd8 --- /dev/null +++ b/audio/filter/filter.c @@ -0,0 +1,360 @@ +/* + * design and implementation of different types of digital filters + * + * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <string.h> +#include <math.h> +#include "dsp.h" + +/****************************************************************************** +* FIR filter implementations +******************************************************************************/ + +/* 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 FLOAT_TYPE af_filter_fir(register unsigned int n, const FLOAT_TYPE* w, + const FLOAT_TYPE* x) +{ + register FLOAT_TYPE y; // Output + y = 0.0; + do{ + n--; + y+=w[n]*x[n]; + }while(n != 0); + return y; +} + +/****************************************************************************** +* FIR filter design +******************************************************************************/ + +/* 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 af_filter_design_fir(unsigned int n, FLOAT_TYPE* w, const FLOAT_TYPE* fc, + unsigned int flags, FLOAT_TYPE 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 + + FLOAT_TYPE k1 = 2 * M_PI; // 2*pi*fc1 + FLOAT_TYPE k2 = 0.5 * (FLOAT_TYPE)(1 - o);// Constant used if the filter has even length + FLOAT_TYPE k3; // 2*pi*fc2 Constant used in BP and BS design + FLOAT_TYPE g = 0.0; // Gain + FLOAT_TYPE t1,t2,t3; // Temporary variables + FLOAT_TYPE fc1,fc2; // Cutoff frequencies + + // Sanity check + if(!w || (n == 0)) return -1; + + // Get window coefficients + switch(flags & WINDOW_MASK){ + case(BOXCAR): + af_window_boxcar(n,w); break; + case(TRIANG): + af_window_triang(n,w); break; + case(HAMMING): + af_window_hamming(n,w); break; + case(HANNING): + af_window_hanning(n,w); break; + case(BLACKMAN): + af_window_blackman(n,w); break; + case(FLATTOP): + af_window_flattop(n,w); break; + case(KAISER): + af_window_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 = (FLOAT_TYPE)(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 = (FLOAT_TYPE)(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 = (FLOAT_TYPE)(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 = (FLOAT_TYPE)(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; +} + +/****************************************************************************** +* IIR filter design +******************************************************************************/ + +/* Helper functions for the bilinear transform */ + +/* Pre-warp the coefficients of a numerator or denominator. + Note that a0 is assumed to be 1, so there is no wrapping + of it. +*/ +static void af_filter_prewarp(FLOAT_TYPE* a, FLOAT_TYPE fc, FLOAT_TYPE fs) +{ + FLOAT_TYPE wp; + wp = 2.0 * fs * tan(M_PI * fc / fs); + a[2] = a[2]/(wp * wp); + a[1] = a[1]/wp; +} + +/* Transform the numerator and denominator coefficients of s-domain + biquad section into corresponding z-domain coefficients. + + The transfer function for z-domain is: + + 1 + alpha1 * z^(-1) + alpha2 * z^(-2) + H(z) = ------------------------------------- + 1 + beta1 * z^(-1) + beta2 * z^(-2) + + Store the 4 IIR coefficients in array pointed by coef in following + order: + beta1, beta2 (denominator) + alpha1, alpha2 (numerator) + + Arguments: + a - s-domain numerator coefficients + b - s-domain denominator coefficients + k - filter gain factor. Initially set to 1 and modified by each + biquad section in such a way, as to make it the + coefficient by which to multiply the overall filter gain + in order to achieve a desired overall filter gain, + specified in initial value of k. + fs - sampling rate (Hz) + coef - array of z-domain coefficients to be filled in. + + Return: On return, set coef z-domain coefficients and k to the gain + required to maintain overall gain = 1.0; +*/ +static void af_filter_bilinear(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE* k, + FLOAT_TYPE fs, FLOAT_TYPE *coef) +{ + FLOAT_TYPE ad, bd; + + /* alpha (Numerator in s-domain) */ + ad = 4. * a[2] * fs * fs + 2. * a[1] * fs + a[0]; + /* beta (Denominator in s-domain) */ + bd = 4. * b[2] * fs * fs + 2. * b[1] * fs + b[0]; + + /* Update gain constant for this section */ + *k *= ad/bd; + + /* Denominator */ + *coef++ = (2. * b[0] - 8. * b[2] * fs * fs)/bd; /* beta1 */ + *coef++ = (4. * b[2] * fs * fs - 2. * b[1] * fs + b[0])/bd; /* beta2 */ + + /* Numerator */ + *coef++ = (2. * a[0] - 8. * a[2] * fs * fs)/ad; /* alpha1 */ + *coef = (4. * a[2] * fs * fs - 2. * a[1] * fs + a[0])/ad; /* alpha2 */ +} + + + +/* IIR filter design using bilinear transform and prewarp. Transforms + 2nd order s domain analog filter into a digital IIR biquad link. To + create a filter fill in a, b, Q and fs and make space for coef and k. + + + Example Butterworth design: + + Below are Butterworth polynomials, arranged as a series of 2nd + order sections: + + Note: n is filter order. + + n Polynomials + ------------------------------------------------------------------- + 2 s^2 + 1.4142s + 1 + 4 (s^2 + 0.765367s + 1) * (s^2 + 1.847759s + 1) + 6 (s^2 + 0.5176387s + 1) * (s^2 + 1.414214 + 1) * (s^2 + 1.931852s + 1) + + For n=4 we have following equation for the filter transfer function: + 1 1 + T(s) = --------------------------- * ---------------------------- + s^2 + (1/Q) * 0.765367s + 1 s^2 + (1/Q) * 1.847759s + 1 + + The filter consists of two 2nd order sections since highest s power + is 2. Now we can take the coefficients, or the numbers by which s + is multiplied and plug them into a standard formula to be used by + bilinear transform. + + Our standard form for each 2nd order section is: + + a2 * s^2 + a1 * s + a0 + H(s) = ---------------------- + b2 * s^2 + b1 * s + b0 + + Note that Butterworth numerator is 1 for all filter sections, which + means s^2 = 0 and s^1 = 0 + + Let's convert standard Butterworth polynomials into this form: + + 0 + 0 + 1 0 + 0 + 1 + --------------------------- * -------------------------- + 1 + ((1/Q) * 0.765367) + 1 1 + ((1/Q) * 1.847759) + 1 + + Section 1: + a2 = 0; a1 = 0; a0 = 1; + b2 = 1; b1 = 0.765367; b0 = 1; + + Section 2: + a2 = 0; a1 = 0; a0 = 1; + b2 = 1; b1 = 1.847759; b0 = 1; + + Q is filter quality factor or resonance, in the range of 1 to + 1000. The overall filter Q is a product of all 2nd order stages. + For example, the 6th order filter (3 stages, or biquads) with + individual Q of 2 will have filter Q = 2 * 2 * 2 = 8. + + + Arguments: + a - s-domain numerator coefficients, a[1] is always assumed to be 1.0 + b - s-domain denominator coefficients + Q - Q value for the filter + k - filter gain factor. Initially set to 1 and modified by each + biquad section in such a way, as to make it the + coefficient by which to multiply the overall filter gain + in order to achieve a desired overall filter gain, + specified in initial value of k. + fs - sampling rate (Hz) + coef - array of z-domain coefficients to be filled in. + + Note: Upon return from each call, the k argument will be set to a + value, by which to multiply our actual signal in order for the gain + to be one. On second call to szxform() we provide k that was + changed by the previous section. During actual audio filtering + k can be used for gain compensation. + + return -1 if fail 0 if success. +*/ +int af_filter_szxform(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE Q, FLOAT_TYPE fc, + FLOAT_TYPE fs, FLOAT_TYPE *k, FLOAT_TYPE *coef) +{ + FLOAT_TYPE at[3]; + FLOAT_TYPE bt[3]; + + if(!a || !b || !k || !coef || (Q>1000.0 || Q< 1.0)) + return -1; + + memcpy(at,a,3*sizeof(FLOAT_TYPE)); + memcpy(bt,b,3*sizeof(FLOAT_TYPE)); + + bt[1]/=Q; + + /* Calculate a and b and overwrite the original values */ + af_filter_prewarp(at, fc, fs); + af_filter_prewarp(bt, fc, fs); + /* Execute bilinear transform */ + af_filter_bilinear(at, bt, k, fs, coef); + + return 0; +} diff --git a/audio/filter/filter.h b/audio/filter/filter.h new file mode 100644 index 0000000000..aed33352c2 --- /dev/null +++ b/audio/filter/filter.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#if !defined MPLAYER_DSP_H +# error Never use filter.h directly; include dsp.h instead. +#endif + +#ifndef MPLAYER_FILTER_H +#define MPLAYER_FILTER_H + + +// 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 +FLOAT_TYPE af_filter_fir(unsigned int n, const FLOAT_TYPE* w, const FLOAT_TYPE* x); + +int af_filter_design_fir(unsigned int n, FLOAT_TYPE* w, const FLOAT_TYPE* fc, + unsigned int flags, FLOAT_TYPE opt); + +int af_filter_szxform(const FLOAT_TYPE* a, const FLOAT_TYPE* b, FLOAT_TYPE Q, + FLOAT_TYPE fc, FLOAT_TYPE fs, FLOAT_TYPE *k, + FLOAT_TYPE *coef); + +/* 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 af_filter_updateq(n,xi,xq,in)\ + xq[xi]=(xq)[(xi)+(n)]=*(in);\ + xi=(++(xi))&((n)-1); + +#endif /* MPLAYER_FILTER_H */ diff --git a/audio/filter/window.c b/audio/filter/window.c new file mode 100644 index 0000000000..a970bdcbea --- /dev/null +++ b/audio/filter/window.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* 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 af_window_boxcar(int n, FLOAT_TYPE* 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 af_window_triang(int n, FLOAT_TYPE* w) +{ + FLOAT_TYPE k1 = (FLOAT_TYPE)(n & 1); + FLOAT_TYPE k2 = 1/((FLOAT_TYPE)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*((FLOAT_TYPE)(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 af_window_hanning(int n, FLOAT_TYPE* w) +{ + int i; + FLOAT_TYPE k = 2*M_PI/((FLOAT_TYPE)(n+1)); // 2*pi/(N+1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.5*(1.0 - cos(k*(FLOAT_TYPE)(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 af_window_hamming(int n,FLOAT_TYPE* w) +{ + int i; + FLOAT_TYPE k = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.54 - 0.46*cos(k*(FLOAT_TYPE)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 af_window_blackman(int n,FLOAT_TYPE* w) +{ + int i; + FLOAT_TYPE k1 = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1) + FLOAT_TYPE k2 = 2*k1; // 4*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.42 - 0.50*cos(k1*(FLOAT_TYPE)i) + 0.08*cos(k2*(FLOAT_TYPE)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 af_window_flattop(int n,FLOAT_TYPE* w) +{ + int i; + FLOAT_TYPE k1 = 2*M_PI/((FLOAT_TYPE)(n-1)); // 2*pi/(N-1) + FLOAT_TYPE k2 = 2*k1; // 4*pi/(N-1) + + // Calculate window coefficients + for (i=0; i<n; i++) + *w++ = 0.2810638602 - 0.5208971735*cos(k1*(FLOAT_TYPE)i) + + 0.1980389663*cos(k2*(FLOAT_TYPE)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 + +static FLOAT_TYPE besselizero(FLOAT_TYPE x) +{ + FLOAT_TYPE temp; + FLOAT_TYPE sum = 1.0; + FLOAT_TYPE u = 1.0; + FLOAT_TYPE halfx = x/2.0; + int n = 1; + + do { + temp = halfx/(FLOAT_TYPE)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 af_window_kaiser(int n, FLOAT_TYPE* w, FLOAT_TYPE b) +{ + FLOAT_TYPE tmp; + FLOAT_TYPE 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 = (FLOAT_TYPE)(2*i + k2) / ((FLOAT_TYPE)n - 1.0); + w[end-(1&(!k2))+i] = w[end-1-i] = k1 * besselizero(b*sqrt(1.0 - tmp*tmp)); + } +} diff --git a/audio/filter/window.h b/audio/filter/window.h new file mode 100644 index 0000000000..1c179b7902 --- /dev/null +++ b/audio/filter/window.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2001 Anders Johansson ajh@atri.curtin.edu.au + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* 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 MPLAYER_DSP_H +# error Never use window.h directly; include dsp.h instead. +#endif + +#ifndef MPLAYER_WINDOW_H +#define MPLAYER_WINDOW_H + +void af_window_boxcar(int n, FLOAT_TYPE* w); +void af_window_triang(int n, FLOAT_TYPE* w); +void af_window_hanning(int n, FLOAT_TYPE* w); +void af_window_hamming(int n, FLOAT_TYPE* w); +void af_window_blackman(int n, FLOAT_TYPE* w); +void af_window_flattop(int n, FLOAT_TYPE* w); +void af_window_kaiser(int n, FLOAT_TYPE* w, FLOAT_TYPE b); + +#endif /* MPLAYER_WINDOW_H */ |