19 - SCL | 19 - SCL | ||||
22 - TX | 22 - TX | ||||
23 - LRCLK | 23 - LRCLK | ||||
*/ | */ | ||||
//#include <arm_math.h> | //#include <arm_math.h> | ||||
AudioInputI2S audioInput; // audio shield: mic or line-in | 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 | AudioOutputI2S audioOutput; // audio shield: headphones & line-out | ||||
// Create Audio connections between the components | // Create Audio connections between the components | ||||
void setup() { | void setup() { | ||||
Serial.begin(9600); | 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); | AudioMemory(8); | ||||
audioShield.enable(); | audioShield.enable(); | ||||
Serial.println(") is grounded"); | Serial.println(") is grounded"); | ||||
} | } | ||||
// Initialize the filter | // 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"); | Serial.println("setup done"); | ||||
} | } | ||||
// audio volume | // audio volume | ||||
int volume = 0; | int volume = 0; | ||||
unsigned long last_time = millis(); | unsigned long last_time = millis(); | ||||
void loop() | void loop() | ||||
{ | { | ||||
// Volume control | // Volume control | ||||
int n = analogRead(15); | int n = analogRead(15); | ||||
if (n != volume) { | if (n != volume) { | ||||
volume = n; | 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 | // update the two buttons | ||||
// If the passthru button is pushed, save the current | // If the passthru button is pushed, save the current | ||||
// filter index and then switch the filter to passthru | // filter index and then switch the filter to passthru | ||||
if(b_passthru.fallingEdge()) { | |||||
if (b_passthru.fallingEdge()) { | |||||
old_idx = fir_idx; | 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 passthru button is released, restore previous filter | ||||
if(b_passthru.risingEdge()) { | |||||
if (b_passthru.risingEdge()) { | |||||
if(old_idx != -1) { | 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; | old_idx = -1; | ||||
} | } | ||||
// switch to next filter in the list | // switch to next filter in the list | ||||
if(b_filter.fallingEdge()) { | |||||
if (b_filter.fallingEdge()) { | |||||
fir_idx++; | 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("Proc = "); | ||||
Serial.print(AudioProcessorUsage()); | Serial.print(AudioProcessorUsage()); | ||||
Serial.print(" ("); | Serial.print(" ("); | ||||
Serial.println(")"); | Serial.println(")"); | ||||
last_time = millis(); | last_time = millis(); | ||||
} | } | ||||
} | |||||
} | } |
#include "filters.h" | #include "filters.h" | ||||
short low_pass[NUM_COEFFS] = { | short low_pass[NUM_COEFFS] = { | ||||
#include "lopass_4000_44100.h" | |||||
#include "lopass_1000_44100.h" | |||||
}; | }; | ||||
short band_pass[NUM_COEFFS] = { | short band_pass[NUM_COEFFS] = { |
#include "filter_fir.h" | #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 there's no coefficient table, give up. | ||||
if(coeff_p == NULL) return; | |||||
if (coeff_p == NULL) { | |||||
release(block); | |||||
return; | |||||
} | |||||
// do passthru | // do passthru | ||||
if (coeff_p == FIR_PASSTHRU) { | if (coeff_p == FIR_PASSTHRU) { | ||||
// Just passthrough | // Just passthrough | ||||
block = receiveReadOnly(0); | |||||
if(block) { | |||||
transmit(block,0); | |||||
release(block); | |||||
} | |||||
transmit(block); | |||||
release(block); | |||||
return; | return; | ||||
} | } | ||||
// Left Channel | |||||
block = receiveReadOnly(0); | |||||
// get a block for the FIR output | // get a block for the FIR output | ||||
b_new = allocate(); | 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); | |||||
} | } | ||||
// Indicates that the code should just pass through the audio | // Indicates that the code should just pass through the audio | ||||
// without any filtering (as opposed to doing nothing at all) | // 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 | class AudioFilterFIR : public AudioStream | ||||
{ | { | ||||
public: | 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); | virtual void update(void); | ||||
void stop(void); | |||||
private: | private: | ||||
audio_block_t *inputQueueArray[1]; | 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 | // 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 | #endif |
<tr class=top><th>Port</th><th>Purpose</th></tr> | <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></td><td></td></tr> | ||||
</table> | </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> | <h3>Functions</h3> | ||||
<p class=func><span class=keyword>begin</span>(delayBuffer, length, n_chorus);</p> | <p class=func><span class=keyword>begin</span>(delayBuffer, length, n_chorus);</p> | ||||
<p class=desc>blah blah blah blah | <p class=desc>blah blah blah blah | ||||
<tr class=top><th>Port</th><th>Purpose</th></tr> | <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></td><td></td></tr> | ||||
</table> | </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> | <h3>Functions</h3> | ||||
<p class=func><span class=keyword>begin</span>(delayBuffer, length, offset, depth, delayRate);</p> | <p class=func><span class=keyword>begin</span>(delayBuffer, length, offset, depth, delayRate);</p> | ||||
<p class=desc>blah blah blah blah | <p class=desc>blah blah blah blah | ||||
</script> | </script> | ||||
<script type="text/x-red" data-help-name="AudioFilterFIR"> | <script type="text/x-red" data-help-name="AudioFilterFIR"> | ||||
<h3>Summary</h3> | <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> | <h3>Audio Connections</h3> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Port</th><th>Purpose</th></tr> | <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> | </table> | ||||
<p>Extra description... Section only present if object has params</p>--> | |||||
<h3>Functions</h3> | <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> | </p> | ||||
<h3>Notes</h3> | <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> | ||||
<script type="text/x-red" data-template-name="AudioFilterFIR"> | <script type="text/x-red" data-template-name="AudioFilterFIR"> | ||||
<div class="form-row"> | <div class="form-row"> | ||||
</script> | </script> | ||||
<script type="text/x-red" data-help-name="AudioAnalyzePeak"> | <script type="text/x-red" data-help-name="AudioAnalyzePeak"> | ||||
<h3>Summary</h3> | <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> | <h3>Audio Connections</h3> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Port</th><th>Purpose</th></tr> | <tr class=top><th>Port</th><th>Purpose</th></tr> | ||||
<script type="text/x-red" data-help-name="AudioAnalyzePrint"> | <script type="text/x-red" data-help-name="AudioAnalyzePrint"> | ||||
<h3>Summary</h3> | <h3>Summary</h3> | ||||
<p>Print raw audio data to the Arduino Serial Monitor. This | <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> | <h3>Audio Connections</h3> | ||||
<table class=doc align=center cellpadding=3> | <table class=doc align=center cellpadding=3> | ||||
<tr class=top><th>Port</th><th>Purpose</th></tr> | <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> | </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> | <h3>Functions</h3> | ||||
<p class=func><span class=keyword>name</span>(string);</p> | <p class=func><span class=keyword>name</span>(string);</p> | ||||
<p class=desc>blah blah blah blah | <p class=desc>blah blah blah blah | ||||
<p class=desc>blah blah blah blah | <p class=desc>blah blah blah blah | ||||
</p> | </p> | ||||
<h3>Notes</h3> | <h3>Notes</h3> | ||||
<p></p> | |||||
<p>This object doesn't work very well and probably should not be used.</p> | |||||
</script> | </script> | ||||
<script type="text/x-red" data-template-name="AudioAnalyzePrint"> | <script type="text/x-red" data-template-name="AudioAnalyzePrint"> | ||||
<div class="form-row"> | <div class="form-row"> |