/***************************************************************************** * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: equalizer.c 271 2005-08-09 08:31:35Z picard $ * * The Core Pocket Media Player * Copyright (c) 2004-2005 Gabor Kovacs * ****************************************************************************/ #include "common.h" #include "fixed.h" #include #define MAXFILTER 10 #define ACCFAST_ADJUST 1 typedef struct eqfilter { fix_t alpha; fix_t beta; fix_t gamma; fix_t alpha0; } eqfilter; typedef struct equalizer { codec Codec; bool_t Enabled; bool_t Attenuate; int Amplify; //-20..20 int Eq[10]; //-20..20 buffer Buffer; int Channels; void* PCM; fix_t Scale; fix_t ScalePreamp; fix_t ScaleFinal; eqfilter Filter[MAXFILTER]; fix_t State[MAXPLANES][3+MAXFILTER*2]; planes Tmp; } equalizer; static const datatable Params[] = { { EQUALIZER_ENABLED, TYPE_BOOL,DF_SETUP|DF_CHECKLIST }, { EQUALIZER_ATTENUATE, TYPE_BOOL,DF_SETUP|DF_CHECKLIST }, { EQUALIZER_PREAMP, TYPE_INT, DF_SETUP|DF_MINMAX|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_1, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL|DF_GAP, -20, 20 }, { EQUALIZER_2, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_3, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_4, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_5, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_6, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_7, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_8, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_9, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_10, TYPE_INT, DF_SETUP|DF_MINMAX|DF_NOWRAP|DF_SHORTLABEL, -20, 20 }, { EQUALIZER_RESET, TYPE_RESET,DF_SETUP|DF_NOSAVE|DF_GAP }, DATATABLE_END(EQUALIZER_ID) }; static int Enum( equalizer* p, int* No, datadef* Param) { if (CodecEnum(&p->Codec,No,Param)==ERR_NONE) return ERR_NONE; return NodeEnumTable(No,Param,Params); } static int Get( equalizer* p, int No, void* Data, int Size ) { int Result = CodecGet(&p->Codec,No,Data,Size); switch (No) { case EQUALIZER_RESET: Result = ERR_NONE; break; case EQUALIZER_ENABLED: GETVALUE(p->Enabled,bool_t); break; case EQUALIZER_ATTENUATE: GETVALUE(p->Attenuate,bool_t); break; case EQUALIZER_PREAMP: GETVALUE(p->Amplify,int); break; case EQUALIZER_1: case EQUALIZER_2: case EQUALIZER_3: case EQUALIZER_4: case EQUALIZER_5: case EQUALIZER_6: case EQUALIZER_7: case EQUALIZER_8: case EQUALIZER_9: case EQUALIZER_10:GETVALUE(p->Eq[No-EQUALIZER_1],int); break; } return Result; } static NOINLINE void UpdateScale(equalizer* p) { fix_t Scale; int n; if (!p->Attenuate) p->Scale=FIXC(1.); if (p->ScaleScale=FIXC(1./65556); Scale = fix_mul(p->Scale,p->ScalePreamp); p->ScaleFinal = FIXFAST_BSHIFT(Scale); for (n=0;nFilter[n].alpha = ACCFAST_BSHIFT(fix_mul(p->Filter[n].alpha0,Scale)); } static const eqfilter Band44100[MAXFILTER] = { { FIXC(0.003013),FIXC(-0.993973),FIXC( 1.993901) }, { FIXC(0.008490),FIXC(-0.983019),FIXC( 1.982437) }, { FIXC(0.015374),FIXC(-0.969252),FIXC( 1.967331) }, { FIXC(0.029328),FIXC(-0.941343),FIXC( 1.934254) }, { FIXC(0.047918),FIXC(-0.904163),FIXC( 1.884869) }, { FIXC(0.130408),FIXC(-0.739184),FIXC( 1.582718) }, { FIXC(0.226555),FIXC(-0.546889),FIXC( 1.015267) }, { FIXC(0.344937),FIXC(-0.310127),FIXC(-0.181410) }, { FIXC(0.366438),FIXC(-0.267123),FIXC(-0.521151) }, { FIXC(0.379009),FIXC(-0.241981),FIXC(-0.808451) }, }; static const fix_t PowTab[37] = //pow(10,(n-18)/20.f)) { FIXC(0.125892541),FIXC(0.141253754),FIXC(0.158489319),FIXC(0.177827941), FIXC(0.199526231),FIXC(0.223872114),FIXC(0.251188643),FIXC(0.281838293), FIXC(0.316227766),FIXC(0.354813389),FIXC(0.398107171),FIXC(0.446683592), FIXC(0.501187234),FIXC(0.562341325),FIXC(0.630957344),FIXC(0.707945784), FIXC(0.794328235),FIXC(0.891250938),FIXC(1.000000000),FIXC(1.122018454), FIXC(1.258925412),FIXC(1.412537545),FIXC(1.584893192),FIXC(1.778279410), FIXC(1.995262315),FIXC(2.238721139),FIXC(2.511886432),FIXC(2.818382931), FIXC(3.162277660),FIXC(3.548133892),FIXC(3.981071706),FIXC(4.466835922), FIXC(5.011872336),FIXC(5.623413252),FIXC(6.309573445),FIXC(7.079457844), FIXC(7.943282347), }; static INLINE fix_t Pow(int n) { if (n<-18) n=-18; if (n>18) n=18; return PowTab[n+18]; } static NOINLINE int UpdateParam(equalizer* p) { if (p->Codec.In.Format.Type == PACKET_AUDIO) { int n; const eqfilter *src; eqfilter *dst; src = Band44100; dst = p->Filter; for (n=0;nalpha0 = fix_mul(src->alpha,Pow(p->Eq[n])-FIXC(1.)); dst->beta = ACCFAST_BSHIFT(src->beta); dst->gamma = ACCFAST_BSHIFT(src->gamma); } p->ScalePreamp = Pow(p->Amplify); UpdateScale(p); } return ERR_NONE; } static NOINLINE int UpdateEqualizer() { context* p = Context(); if (!p->Player) return ERR_NONE; return p->Player->Set(p->Player,PLAYER_UPDATEEQUALIZER,NULL,0); } static int Set( equalizer* p, int No, const void* Data, int Size ) { int Result = CodecSet(&p->Codec,No,Data,Size); switch (No) { case EQUALIZER_ENABLED: SETVALUE(p->Enabled,bool_t,UpdateEqualizer()); break; case EQUALIZER_ATTENUATE: SETVALUE(p->Attenuate,int,UpdateParam(p)); break; case EQUALIZER_PREAMP: SETVALUE(p->Amplify,int,UpdateParam(p)); break; case EQUALIZER_1: case EQUALIZER_2: case EQUALIZER_3: case EQUALIZER_4: case EQUALIZER_5: case EQUALIZER_6: case EQUALIZER_7: case EQUALIZER_8: case EQUALIZER_9: case EQUALIZER_10:SETVALUE(p->Eq[No-EQUALIZER_1],int,UpdateParam(p)); break; } return Result; } static int Flush(equalizer* p) { memset(p->State,0,sizeof(p->State)); return ERR_NONE; } static int UpdateInput(equalizer* p) { PCMRelease(p->PCM); p->PCM = NULL; BufferClear(&p->Buffer); if (p->Codec.In.Format.Type == PACKET_AUDIO) { if (!p->Enabled || p->Codec.In.Format.Format.Audio.Channels>MAXPLANES) return ERR_INVALID_PARAM; PacketFormatPCM(&p->Codec.Out.Format,&p->Codec.In.Format,FIX_FRACBITS+1); p->Codec.Out.Format.Format.Audio.Bits = sizeof(fix_t)*8; p->Codec.Out.Format.Format.Audio.Flags = PCM_PLANES; #ifndef FIXED_POINT p->Codec.Out.Format.Format.Audio.Flags |= PCM_FLOAT; #endif p->PCM = PCMCreate(&p->Codec.Out.Format.Format.Audio,&p->Codec.In.Format.Format.Audio,0,0); if (!p->PCM) return ERR_OUT_OF_MEMORY; p->Scale = FIXC(1); UpdateParam(p); Flush(p); } return ERR_NONE; } static NOINLINE void Rescale(planes data, fix_t v, int ramp, size_t length, int channels) { fix_t dv; int n; if (length < 4*sizeof(fix_t)) return; while ((sizeof(fix_t)< length) --ramp; #ifdef FIXED_POINT dv = (v-FIXC(1)) >> ramp; #else dv = (fix_t)ldexp(v-FIXC(1),-ramp); #endif for (n=0;nCodec.In.Format.Format.Audio.Channels; SrcLength = Packet->Length; DstLength = PCMDstLength(p->PCM,SrcLength); if (DstLength*Channels*2 > p->Buffer.Allocated) { uint8_t* Data; if (!BufferAlloc(&p->Buffer,DstLength*Channels*2,1024)) return ERR_OUT_OF_MEMORY; Data = p->Buffer.Data; for (n=0;nCodec.Packet.Data[n] = Data; Data += DstLength; p->Tmp[n] = Data; Data += DstLength; } } PCMConvert(p->PCM,*(planes*)&p->Codec.Packet.Data,Packet->Data,&DstLength,&SrcLength,SPEED_ONE,256); p->Codec.Packet.RefTime = Packet->RefTime; p->Codec.Packet.Length = DstLength; s = p->State[0]; for (n=0;nCodec.Packet.Data[n]; fix_t* ie=(fix_t*)((uint8_t*)i+DstLength); fix_t* j=(fix_t*)p->Tmp[n]; fix_t a,b,c,d,e; e = p->ScaleFinal; a = s[2]; b = s[1]; c = s[0]; for (ie-=3;iFilter;f!=p->Filter+MAXFILTER;f+=2,s+=2*2) if (f[0].alpha!=0 || f[1].alpha!=0) { accfast_var(v0); accfast_var(v1); i=(fix_t*)p->Codec.Packet.Data[n]; ie=(fix_t*)((uint8_t*)i+DstLength); j=(fix_t*)p->Tmp[n]; a = s[0]; c = s[1]; b = s[2]; d = s[3]; for (--ie;iAttenuate) { fix_t Max = 0; for (n=0;nCodec.Packet.Data[n]; fix_t* ie=(fix_t*)((uint8_t*)i+DstLength); for (;i!=ie;++i) { fix_t v = *i; if (v<0) v=-v; if (v>Max) Max=v; } } if (Max > FIXC(1.)) { Max = fix_mul(fix_1div(Max),FIXC(15./16)); Rescale(*(planes*)&p->Codec.Packet.Data,Max,5,DstLength,Channels); p->Scale = fix_mul(p->Scale,Max); UpdateScale(p); } else if (Max < FIXC(6./8) && p->Scale < FIXC(1-1./256)) { Max = FIXC(32./31); if (fix_mul(p->Scale,Max) > FIXC(1)) Max = fix_1div(p->Scale); Rescale(*(planes*)&p->Codec.Packet.Data,Max,10,DstLength,Channels); p->Scale = fix_mul(p->Scale,Max); UpdateScale(p); } } else if (p->Scale != FIXC(1.)) UpdateScale(p); return ERR_NONE; } static int Create(equalizer* p) { p->Codec.Node.Enum = (nodeenum)Enum; p->Codec.Node.Set = (nodeset)Set; p->Codec.Node.Get = (nodeget)Get; p->Codec.Process = (packetprocess)Process; p->Codec.UpdateInput = (nodefunc)UpdateInput; p->Codec.Flush = (nodefunc)Flush; return ERR_NONE; } static const nodedef Equalizer = { sizeof(equalizer)|CF_GLOBAL|CF_SETTINGS, EQUALIZER_ID, CODEC_CLASS, PRI_MAXIMUM+580, (nodecreate)Create, }; void Equalizer_Init() { NodeRegisterClass(&Equalizer); } void Equalizer_Done() { NodeUnRegisterClass(EQUALIZER_ID); }