Om det bara är 8 band så tror jag nog att ett gäng bandpassfilter skulle vara snabbare än FFT. Efter bandpassfiltrena behöver du hänga på en så kallad "envelope follower", vilket i princip är ett lågpassfilter. Skillnaden är att man har olika koefficienter beroende på om värdet ökar eller minskar för att kunna ha en snabb "attack" och långsam "release". Jag skulle rekomentera omkring 5-10mS attack och 500-1500mS release. Om man tar för givet att högsta frekvensen du vill kunna mäta är på 16kHz kommer samplingsfrekvensen behöva ligga på minst 32kHz. Du kan du läsa varför
Här.
Nu har jag inte stenkoll på PIC, men om man tar för givet att en PIC kan utföra 20M instruktioner per sekund så kommer man altså att ha cirka 78 instruktioner på sig per band för varje sample som skall beräknas. Om en PIC kan multiplicera heltal på en instruktion så borde det inte vara några problem.
Här har du ett bandpass-filter att börja labba med:
Kod: Markera allt
typedef struct {
float in1, in2;
float out1, out2;
float a1a0, a2a0, b0a0, b1a0, b2a0;
} FILTER;
void filter_init(FILTER *f) {
float bandwidth = 0.5f; // Bandbredd i oktaver
float frequency = 440.0f; // Frekvens i Hz
float samplerate = 32000.0f; // Samplingsfrekvens i Hz
f->in1 = 0.0f;
f->in2 = 0.0f;
f->out1 = 0.0f;
f->out2 = 0.0f;
float w0 = 2.0f * PI * frequency / samplerate;
float alpha = sinf(w0) * sinhf(logf(2.0f) / 2.0f * bandwidth * w0 / sinf(w0));
float b0 = alpha;
float b1 = 0;
float b2 = -alpha;
float a0 = 1 + alpha;
float a1 = -2 * cosf(w0);
float a2 = 1 - alpha;
f->a1a0 = a1 / a0;
f->a2a0 = a2 / a0;
f->b0a0 = b0 / a0;
f->b1a0 = b1 / a0;
f->b2a0 = b2 / a0;
}
float filter_process(float in) {
return (f->b0a0) * in + (f->b1a0) * f->in1 + (f->b2a0) * f->in2 - (f->a1a0) * f->out1 - (f->a2a0) * f->out2;
}
Kör filter_init() en gång i början och peta in rätt variabler (de tre översta). Kör sedan filter_process() en gång för varje sample. Självklart blir det ju lite tungt med floating points på en PIC, så du får ju ändra koden till fixed point
Du kan få en envelope follower när vi ändå är igång...
Kod: Markera allt
float attack_in_ms = 10.0f, release_in_ms = 1000.0f;
float attack_coef, release_coef, level = 0.0f;
void env_init() {
attack_coef = pow(0.01, 1.0 / (attack_in_ms * samplerate * 0.001));
release_coef = pow(0.01, 1.0 / (release_in_ms * samplerate * 0.001));
}
float env_process(float input) {
abs_in = fabs(input);
if(abs_in > level) {
level = attack_coef * (level - abs_in) + abs_in;
} else {
level = release_coef * (level - abs_in) + abs_in;
}
return level;
}
(Kan hända att koden inte kompilerar rakt av då jag cut 'n paste:ade en hel del och ändrade utan att testa den.)
Säg till om du behöver mer hjälp!
EDIT: Filtret kommer
härifrån. Finns en hel del andra filter som kan vara värta att titta på. Till exempel kan man ju använda sig av ett gäng högpass + lågpass för att få ut de 8 banden.
EDIT2: För övrigt så heter det inte equalizer, det heter spektrumsanalysator (eng: spectrum analyzer).
EDIT3: filter_init() ska självklart inte köras på själva PIC:en. Räkna ut dessa värden på en dator och spara som statiska värden i minnet på din PIC.