@@ -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,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] = { |
@@ -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); | |||
} | |||
@@ -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 |
@@ -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"> |