| @@ -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"> | |||