Browse Source

Impove and document FIR filter

dds
PaulStoffregen 10 years ago
parent
commit
f7f896195a
7 changed files with 98 additions and 116 deletions
  1. +24
    -30
      examples/Effects/Filter_FIR/Filter_FIR.ino
  2. +1
    -1
      examples/Effects/Filter_FIR/filters.cpp
  3. +0
    -0
      examples/Effects/Filter_FIR/lopass_1000_44100.h
  4. +17
    -37
      filter_fir.cpp
  5. +19
    -15
      filter_fir.h
  6. BIN
      gui/fir_filter.png
  7. +37
    -33
      gui/list.html

+ 24
- 30
examples/Effects/Filter_FIR/Filter_FIR.ino View File

@@ -18,7 +18,6 @@ The audio board uses the following pins.
19 - SCL
22 - TX
23 - LRCLK

*/

//#include <arm_math.h>
@@ -46,9 +45,8 @@ const int myInput = AUDIO_INPUT_LINEIN;


AudioInputI2S audioInput; // audio shield: mic or line-in
// Use the fast FIR filter for left and right channels
AudioFilterFIR myFilterL(true);
AudioFilterFIR myFilterR(true);
AudioFilterFIR myFilterL;
AudioFilterFIR myFilterR;
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Create Audio connections between the components
@@ -76,15 +74,13 @@ struct fir_filter fir_list[] = {


void setup() {
Serial.begin(9600);
while (!Serial) ;
delay(3000);
delay(300);

pinMode(PASSTHRU_PIN,INPUT_PULLUP);
pinMode(FILTER_PIN,INPUT_PULLUP);
pinMode(PASSTHRU_PIN, INPUT_PULLUP);
pinMode(FILTER_PIN, INPUT_PULLUP);

// It doesn't work properly with any less than 8
// allocate memory for the audio library
AudioMemory(8);

audioShield.enable();
@@ -105,8 +101,8 @@ void setup() {
Serial.println(") is grounded");
}
// Initialize the filter
myFilterL.begin(fir_list[0].coeffs,fir_list[0].num_coeffs);
myFilterR.begin(fir_list[0].coeffs,fir_list[0].num_coeffs);
myFilterL.begin(fir_list[0].coeffs, fir_list[0].num_coeffs);
myFilterR.begin(fir_list[0].coeffs, fir_list[0].num_coeffs);
Serial.println("setup done");
}

@@ -116,13 +112,15 @@ int old_idx = -1;
// audio volume
int volume = 0;
unsigned long last_time = millis();

void loop()
{
// Volume control
int n = analogRead(15);
if (n != volume) {
volume = n;
audioShield.volume((float)n / 1023);
//uncomment this line if your audio shield has the volume pot
//audioShield.volume((float)n / 1023);
}
// update the two buttons
@@ -131,35 +129,32 @@ void loop()
// If the passthru button is pushed, save the current
// filter index and then switch the filter to passthru
if(b_passthru.fallingEdge()) {
if (b_passthru.fallingEdge()) {
old_idx = fir_idx;
myFilterL.begin(FIR_PASSTHRU,0);
myFilterR.begin(FIR_PASSTHRU,0);
myFilterL.begin(FIR_PASSTHRU, 0);
myFilterR.begin(FIR_PASSTHRU, 0);
}
// If passthru button is released, restore previous filter
if(b_passthru.risingEdge()) {
if (b_passthru.risingEdge()) {
if(old_idx != -1) {
myFilterL.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
myFilterR.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
myFilterL.begin(fir_list[fir_idx].coeffs, fir_list[fir_idx].num_coeffs);
myFilterR.begin(fir_list[fir_idx].coeffs, fir_list[fir_idx].num_coeffs);
}
old_idx = -1;
}
// switch to next filter in the list
if(b_filter.fallingEdge()) {
if (b_filter.fallingEdge()) {
fir_idx++;
if(fir_list[fir_idx].num_coeffs == 0)fir_idx = 0;
myFilterL.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
myFilterR.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
if (fir_list[fir_idx].num_coeffs == 0) fir_idx = 0;
myFilterL.begin(fir_list[fir_idx].coeffs, fir_list[fir_idx].num_coeffs);
myFilterR.begin(fir_list[fir_idx].coeffs, fir_list[fir_idx].num_coeffs);
}


if(1) {
// With fast_fir
// Proc = 18 (18), Mem = 4 (6)

if(millis() - last_time >= 5000) {
// print information about resource usage
// Proc = 18 (18), Mem = 4 (5)
if (millis() - last_time >= 2500) {
Serial.print("Proc = ");
Serial.print(AudioProcessorUsage());
Serial.print(" (");
@@ -171,7 +166,6 @@ if(1) {
Serial.println(")");
last_time = millis();
}
}


}

+ 1
- 1
examples/Effects/Filter_FIR/filters.cpp View File

@@ -1,6 +1,6 @@
#include "filters.h"
short low_pass[NUM_COEFFS] = {
#include "lopass_4000_44100.h"
#include "lopass_1000_44100.h"
};

short band_pass[NUM_COEFFS] = {

examples/Effects/Filter_FIR/lopass_4000_44100.h → examples/Effects/Filter_FIR/lopass_1000_44100.h View File


+ 17
- 37
filter_fir.cpp View File

@@ -22,57 +22,37 @@

#include "filter_fir.h"

void AudioFilterFIR::begin(short *cp, int n_coeffs)
{
// pointer to coefficients
coeff_p = cp;
// Initialize FIR instances for the left and right channels
if(coeff_p && (coeff_p != FIR_PASSTHRU)) {
arm_fir_init_q15(&l_fir_inst, n_coeffs, coeff_p,
&l_StateQ15[0], AUDIO_BLOCK_SAMPLES);
}
}

// This has the same effect as begin(NULL,0);
void AudioFilterFIR::stop(void)
void AudioFilterFIR::update(void)
{
coeff_p = NULL;
}
audio_block_t *block, *b_new;

block = receiveReadOnly();
if (!block) return;

void AudioFilterFIR::update(void)
{
audio_block_t *block,*b_new;
// If there's no coefficient table, give up.
if(coeff_p == NULL) return;
if (coeff_p == NULL) {
release(block);
return;
}

// do passthru
if (coeff_p == FIR_PASSTHRU) {
// Just passthrough
block = receiveReadOnly(0);
if(block) {
transmit(block,0);
release(block);
}
transmit(block);
release(block);
return;
}
// Left Channel
block = receiveReadOnly(0);

// get a block for the FIR output
b_new = allocate();
if (block && b_new) {
if (arm_fast) {
arm_fir_fast_q15(&l_fir_inst, (q15_t *)block->data,
(q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
} else {
arm_fir_q15(&l_fir_inst, (q15_t *)block->data,
(q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
}
transmit(b_new,0); // send the FIR output
if (b_new) {
arm_fir_fast_q15(&fir_inst, (q15_t *)block->data,
(q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
transmit(b_new); // send the FIR output
release(b_new);
}
if (block) release(block);
if (b_new) release(b_new);
release(block);
}



+ 19
- 15
filter_fir.h View File

@@ -28,32 +28,36 @@

// Indicates that the code should just pass through the audio
// without any filtering (as opposed to doing nothing at all)
#define FIR_PASSTHRU ((short *) 1)
#define FIR_PASSTHRU ((const short *) 1)

#define FIR_MAX_COEFFS 120
#define FIR_MAX_COEFFS 200

class AudioFilterFIR : public AudioStream
{
public:
AudioFilterFIR(const boolean a_f): AudioStream(1,inputQueueArray),
arm_fast(a_f), coeff_p(NULL) {
AudioFilterFIR(void): AudioStream(1,inputQueueArray), coeff_p(NULL) {
}
void begin(const short *cp, int n_coeffs) {
coeff_p = cp;
// Initialize FIR instance (ARM DSP Math Library)
if (coeff_p && (coeff_p != FIR_PASSTHRU) && n_coeffs <= FIR_MAX_COEFFS) {
arm_fir_init_q15(&fir_inst, n_coeffs, (q15_t *)coeff_p,
&StateQ15[0], AUDIO_BLOCK_SAMPLES);
}
}
void end(void) {
coeff_p = NULL;
}
void begin(short *coeff_p,int n_coeffs);
virtual void update(void);
void stop(void);
private:
audio_block_t *inputQueueArray[1];
// arm state arrays and FIR instances for left and right channels
// the state arrays are defined to handle a maximum of MAX_COEFFS
// coefficients in a filter
q15_t l_StateQ15[AUDIO_BLOCK_SAMPLES + FIR_MAX_COEFFS];

// Whether to use the fast arm FIR code
const boolean arm_fast;
// pointer to current coefficients or NULL or FIR_PASSTHRU
short *coeff_p;
arm_fir_instance_q15 l_fir_inst;
const short *coeff_p;

// ARM DSP Math library filter instance
arm_fir_instance_q15 fir_inst;
q15_t StateQ15[AUDIO_BLOCK_SAMPLES + FIR_MAX_COEFFS];
};

#endif

BIN
gui/fir_filter.png View File

Before After
Width: 240  |  Height: 93  |  Size: 3.6KB

+ 37
- 33
gui/list.html View File

@@ -1122,12 +1122,6 @@ The actual packets are taken
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center></td><td></td></tr>
</table>
<!--<h3>Parameters</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr>
<tr class=odd><td align=center></td><td>Integer</td><td></td></tr>
</table>
<p>Extra description... Section only present if object has params</p>-->
<h3>Functions</h3>
<p class=func><span class=keyword>begin</span>(delayBuffer, length, n_chorus);</p>
<p class=desc>blah blah blah blah
@@ -1167,12 +1161,6 @@ The actual packets are taken
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center></td><td></td></tr>
</table>
<!--<h3>Parameters</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr>
<tr class=odd><td align=center></td><td>Integer</td><td></td></tr>
</table>
<p>Extra description... Section only present if object has params</p>-->
<h3>Functions</h3>
<p class=func><span class=keyword>begin</span>(delayBuffer, length, offset, depth, delayRate);</p>
<p class=desc>blah blah blah blah
@@ -1451,24 +1439,45 @@ The actual packets are taken
</script>
<script type="text/x-red" data-help-name="AudioFilterFIR">
<h3>Summary</h3>
<p>description</p>
<div>
<p>Finite impulse response filter, useful for all sorts of filtering.
</p>
<p align=center><img src="fir_filter.png"></p>
</div>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center></td><td></td></tr>
</table>
<!--<h3>Parameters</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr>
<tr class=odd><td align=center></td><td>Integer</td><td></td></tr>
<tr class=odd><td align=center>In 0</td><td>Signal to be filtered</td></tr>
<tr class=odd><td align=center>Out 0</td><td>Filtered Signal Output</td></tr>
</table>
<p>Extra description... Section only present if object has params</p>-->
<h3>Functions</h3>
<p class=func><span class=keyword>function</span>(parm1, parm2);</p>
<p class=desc>blah blah blah blah
<p class=func><span class=keyword>begin</span>(array, length);</p>
<p class=desc>Initialize the filter. The array must be 16 bit integers (the
filter's impulse response), and
length indicates the number of points in the array. Array may also be
FIR_PASSTHRU (length = 0), to directly pass the input to output without
filtering.
</p>
<p class=func><span class=keyword>end</span>();</p>
<p class=desc>Turn the filter off.
</p>
<h3>Notes</h3>
<p></p>
<p>FIR filters requires more CPU time than Biquad (IIR), but they can
implement filters with better phase response.
</p>
<p>A 100 point filter requires 9% CPU time on Teensy 3.1. The maximum
supported filter length is 200 points.
</p>
<p>The free
<a href="http://t-filter.appspot.com/fir/index.html" target="_blank"> TFilter Design Tool</a>
can be used to create the impulse response array. Be sure to set the sampling
frequency to 44117 HZ (it defaults to only 2000 Hz) and the output type to "int" (16 bit).
</p>
<p>
If you use TFilter Design's "C/C++ array" option, it's output has "int" definition, which
is 32 bits on Teensy 3.1. Edit "int" to "short" for an array of 16 bit numbers,
and add "const" to avoid consuming extra RAM.
</p>
</script>
<script type="text/x-red" data-template-name="AudioFilterFIR">
<div class="form-row">
@@ -1558,7 +1567,8 @@ The actual packets are taken
</script>
<script type="text/x-red" data-help-name="AudioAnalyzePeak">
<h3>Summary</h3>
<p>description</p>
<p>Track the signal peak amplitude. Very useful for simple
audio level response projects, and general troubleshooting.</p>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
@@ -1808,18 +1818,12 @@ The actual packets are taken
<script type="text/x-red" data-help-name="AudioAnalyzePrint">
<h3>Summary</h3>
<p>Print raw audio data to the Arduino Serial Monitor. This
object creates massive output quickly, and should not normall be used.</p>
object creates massive output quickly, and should not normally be used.</p>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center></td><td></td></tr>
<tr class=odd><td align=center>In 0</td><td>Signal to print</td></tr>
</table>
<!--<h3>Parameters</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Name</th><th>Type</th><th>Function</th></tr>
<tr class=odd><td align=center></td><td>Integer</td><td></td></tr>
</table>
<p>Extra description... Section only present if object has params</p>-->
<h3>Functions</h3>
<p class=func><span class=keyword>name</span>(string);</p>
<p class=desc>blah blah blah blah
@@ -1837,7 +1841,7 @@ The actual packets are taken
<p class=desc>blah blah blah blah
</p>
<h3>Notes</h3>
<p></p>
<p>This object doesn't work very well and probably should not be used.</p>
</script>
<script type="text/x-red" data-template-name="AudioAnalyzePrint">
<div class="form-row">

Loading…
Cancel
Save