| @@ -3,54 +3,39 @@ | |||
| #include <SPI.h> | |||
| #include <SD.h> | |||
| const int myInput = AUDIO_INPUT_LINEIN; | |||
| //const int myInput = AUDIO_INPUT_MIC; | |||
| // each filter requires a set up parameters | |||
| // http://forum.pjrc.com/threads/24793-Audio-Library?p=40179&viewfull=1#post40179 | |||
| // | |||
| int myFilterParameters[] = { // lowpass, Fc=800 Hz, Q=0.707 | |||
| 3224322, 6448644, 3224322, 1974735214, -913890679, 0, 0, 0}; | |||
| // Create the Audio components. These should be created in the | |||
| // order data flows, inputs/sources -> processing -> outputs | |||
| // | |||
| AudioInputI2S audioInput; // audio shield: mic or line-in | |||
| AudioFilterBiquad myFilter(myFilterParameters); | |||
| AudioOutputI2S audioOutput; // audio shield: headphones & line-out | |||
| // Create Audio connections between the components | |||
| // | |||
| AudioConnection c1(audioInput, 0, audioOutput, 0); | |||
| AudioConnection c2(audioInput, 0, myFilter, 0); | |||
| AudioConnection c3(myFilter, 0, audioOutput, 1); | |||
| // GUItool: begin automatically generated code | |||
| AudioInputI2S i2s1; //xy=99,60 | |||
| AudioFilterBiquad biquad1; //xy=257,60 | |||
| AudioOutputI2S i2s2; //xy=416,60 | |||
| AudioConnection patchCord1(i2s1, 0, biquad1, 0); | |||
| AudioConnection patchCord2(biquad1, 0, i2s2, 0); | |||
| AudioConnection patchCord3(biquad1, 0, i2s2, 1); | |||
| AudioControlSGTL5000 sgtl5000_1; //xy=305,132 | |||
| // GUItool: end automatically generated code | |||
| // Create an object to control the audio shield. | |||
| // | |||
| AudioControlSGTL5000 audioShield; | |||
| const int myInput = AUDIO_INPUT_LINEIN; | |||
| //const int myInput = AUDIO_INPUT_MIC; | |||
| void setup() { | |||
| // Audio connections require memory to work. For more | |||
| // detailed information, see the MemoryAndCpuUsage example | |||
| AudioMemory(12); | |||
| // Enable the audio shield and set the output volume. | |||
| audioShield.enable(); | |||
| audioShield.inputSelect(myInput); | |||
| audioShield.volume(0.6); | |||
| sgtl5000_1.enable(); // Enable the audio shield | |||
| sgtl5000_1.inputSelect(myInput); | |||
| sgtl5000_1.volume(0.5); | |||
| // Butterworth filter, 12 db/octave | |||
| biquad1.setLowpass(0, 800, 0.707); | |||
| // Linkwitz-Riley filter, 48 dB/octave | |||
| //biquad1.setLowpass(0, 800, 0.54); | |||
| //biquad1.setLowpass(1, 800, 1.3); | |||
| //biquad1.setLowpass(2, 800, 0.54); | |||
| //biquad1.setLowpass(3, 800, 1.3); | |||
| } | |||
| elapsedMillis volmsec=0; | |||
| void loop() { | |||
| // every 50 ms, adjust the volume | |||
| if (volmsec > 50) { | |||
| float vol = analogRead(15); | |||
| vol = vol / 1024; | |||
| audioShield.volume(vol); | |||
| volmsec = 0; | |||
| } | |||
| } | |||
| @@ -30,8 +30,8 @@ | |||
| void AudioFilterBiquad::update(void) | |||
| { | |||
| audio_block_t *block; | |||
| int32_t a0, a1, a2, b1, b2, sum; | |||
| uint32_t in2, out2, aprev, bprev, flag; | |||
| int32_t b0, b1, b2, a1, a2, sum; | |||
| uint32_t in2, out2, bprev, aprev, flag; | |||
| uint32_t *data, *end; | |||
| int32_t *state; | |||
| block = receiveWritable(); | |||
| @@ -39,65 +39,58 @@ void AudioFilterBiquad::update(void) | |||
| end = (uint32_t *)(block->data) + AUDIO_BLOCK_SAMPLES/2; | |||
| state = (int32_t *)definition; | |||
| do { | |||
| a0 = *state++; | |||
| a1 = *state++; | |||
| a2 = *state++; | |||
| b0 = *state++; | |||
| b1 = *state++; | |||
| b2 = *state++; | |||
| aprev = *state++; | |||
| a1 = *state++; | |||
| a2 = *state++; | |||
| bprev = *state++; | |||
| aprev = *state++; | |||
| sum = *state & 0x3FFF; | |||
| data = end - AUDIO_BLOCK_SAMPLES/2; | |||
| do { | |||
| in2 = *data; | |||
| sum = signed_multiply_accumulate_32x16b(sum, a0, in2); | |||
| sum = signed_multiply_accumulate_32x16t(sum, a1, aprev); | |||
| sum = signed_multiply_accumulate_32x16b(sum, a2, aprev); | |||
| sum = signed_multiply_accumulate_32x16b(sum, b0, in2); | |||
| sum = signed_multiply_accumulate_32x16t(sum, b1, bprev); | |||
| sum = signed_multiply_accumulate_32x16b(sum, b2, bprev); | |||
| out2 = (uint32_t)sum >> 14; | |||
| sum = signed_multiply_accumulate_32x16t(sum, a1, aprev); | |||
| sum = signed_multiply_accumulate_32x16b(sum, a2, aprev); | |||
| out2 = signed_saturate_rshift(sum, 16, 14); | |||
| sum &= 0x3FFF; | |||
| sum = signed_multiply_accumulate_32x16t(sum, a0, in2); | |||
| sum = signed_multiply_accumulate_32x16b(sum, a1, in2); | |||
| sum = signed_multiply_accumulate_32x16t(sum, a2, aprev); | |||
| sum = signed_multiply_accumulate_32x16b(sum, b1, out2); | |||
| sum = signed_multiply_accumulate_32x16t(sum, b0, in2); | |||
| sum = signed_multiply_accumulate_32x16b(sum, b1, in2); | |||
| sum = signed_multiply_accumulate_32x16t(sum, b2, bprev); | |||
| aprev = in2; | |||
| bprev = pack_16x16(sum >> 14, out2); | |||
| sum = signed_multiply_accumulate_32x16b(sum, a1, out2); | |||
| sum = signed_multiply_accumulate_32x16t(sum, a2, aprev); | |||
| bprev = in2; | |||
| aprev = pack_16x16( | |||
| signed_saturate_rshift(sum, 16, 14), out2); | |||
| sum &= 0x3FFF; | |||
| aprev = in2; | |||
| *data++ = bprev; | |||
| bprev = in2; | |||
| *data++ = aprev; | |||
| } while (data < end); | |||
| flag = *state & 0x80000000; | |||
| *state++ = sum | flag; | |||
| *(state-2) = bprev; | |||
| *(state-3) = aprev; | |||
| *(state-2) = aprev; | |||
| *(state-3) = bprev; | |||
| } while (flag); | |||
| transmit(block); | |||
| release(block); | |||
| } | |||
| void AudioFilterBiquad::updateCoefs(uint8_t set,int *source, bool doReset) | |||
| void AudioFilterBiquad::setCoefficients(uint32_t stage, const int *coefficients) | |||
| { | |||
| int32_t *dest=(int32_t *)definition; | |||
| while(set) | |||
| { | |||
| dest+=7; | |||
| if(!(*dest)&0x80000000) return; | |||
| dest++; | |||
| set--; | |||
| } | |||
| if (stage >= 4) return; | |||
| int32_t *dest = definition + (stage << 3); | |||
| __disable_irq(); | |||
| for(uint8_t index=0;index<5;index++) | |||
| { | |||
| *dest++=*source++; | |||
| } | |||
| if(doReset) | |||
| { | |||
| *dest++=0; | |||
| *dest++=0; | |||
| *dest++=0; | |||
| } | |||
| if (stage > 0) *(dest - 1) |= 0x80000000; | |||
| *dest++ = *coefficients++; | |||
| *dest++ = *coefficients++; | |||
| *dest++ = *coefficients++; | |||
| *dest++ = *coefficients++ * -1; | |||
| *dest++ = *coefficients++ * -1; | |||
| *dest++ = 0; | |||
| *dest++ = 0; | |||
| *dest &= 0x80000000; | |||
| __enable_irq(); | |||
| } | |||
| @@ -32,17 +32,85 @@ | |||
| class AudioFilterBiquad : public AudioStream | |||
| { | |||
| public: | |||
| AudioFilterBiquad(int *parameters) | |||
| : AudioStream(1, inputQueueArray), definition(parameters) { } | |||
| AudioFilterBiquad(void) : AudioStream(1, inputQueueArray) { | |||
| // by default, the filter will not pass anything | |||
| for (int i=0; i<32; i++) definition[i] = 0; | |||
| } | |||
| virtual void update(void); | |||
| void updateCoefs(uint8_t set,int *source, bool doReset); | |||
| void updateCoefs(uint8_t set,int *source) { updateCoefs(set,source,false); } | |||
| void updateCoefs(int *source, bool doReset) { updateCoefs(0,source,doReset); } | |||
| void updateCoefs(int *source) { updateCoefs(0,source,false); } | |||
| // Set the biquad coefficients directly | |||
| void setCoefficients(uint32_t stage, const int *coefficients); | |||
| void setCoefficients(uint32_t stage, const double *coefficients) { | |||
| int coef[5]; | |||
| coef[0] = coefficients[0] * 1073741824.0; | |||
| coef[1] = coefficients[1] * 1073741824.0; | |||
| coef[2] = coefficients[2] * 1073741824.0; | |||
| coef[3] = coefficients[3] * 1073741824.0; | |||
| coef[4] = coefficients[4] * 1073741824.0; | |||
| setCoefficients(stage, coef); | |||
| } | |||
| // Compute common filter functions | |||
| // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt | |||
| void setLowpass(uint32_t stage, float frequency, float q = 0.7071) { | |||
| int coef[5]; | |||
| double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); | |||
| double sinW0 = sin(w0); | |||
| double alpha = sinW0 / ((double)q * 2.0); | |||
| double cosW0 = cos(w0); | |||
| double scale = 1073741824.0 / (1.0 + alpha); | |||
| /* b0 */ coef[0] = ((1.0 - cosW0) / 2.0) * scale; | |||
| /* b1 */ coef[1] = (1.0 - cosW0) * scale; | |||
| /* b2 */ coef[2] = coef[0]; | |||
| /* a1 */ coef[3] = (-2.0 * cosW0) * scale; | |||
| /* a2 */ coef[4] = (1.0 - alpha) * scale; | |||
| setCoefficients(stage, coef); | |||
| } | |||
| void setHighpass(uint32_t stage, float frequency, float q = 0.7071) { | |||
| int coef[5]; | |||
| double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); | |||
| double sinW0 = sin(w0); | |||
| double alpha = sinW0 / ((double)q * 2.0); | |||
| double cosW0 = cos(w0); | |||
| double scale = 1073741824.0 / (1.0 + alpha); | |||
| /* b0 */ coef[0] = ((1.0 + cosW0) / 2.0) * scale; | |||
| /* b1 */ coef[1] = -(1.0 + cosW0) * scale; | |||
| /* b2 */ coef[2] = coef[0]; | |||
| /* a1 */ coef[3] = (-2.0 * cosW0) * scale; | |||
| /* a2 */ coef[4] = (1.0 - alpha) * scale; | |||
| setCoefficients(stage, coef); | |||
| } | |||
| void setBandpass(uint32_t stage, float frequency, float q = 1.0) { | |||
| int coef[5]; | |||
| double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); | |||
| double sinW0 = sin(w0); | |||
| double alpha = sinW0 / ((double)q * 2.0); | |||
| double cosW0 = cos(w0); | |||
| double scale = 1073741824.0 / (1.0 + alpha); | |||
| /* b0 */ coef[0] = alpha * scale; | |||
| /* b1 */ coef[1] = 0; | |||
| /* b2 */ coef[2] = (-alpha) * scale; | |||
| /* a1 */ coef[3] = (-2.0 * cosW0) * scale; | |||
| /* a2 */ coef[4] = (1.0 - alpha) * scale; | |||
| setCoefficients(stage, coef); | |||
| } | |||
| void setNotch(uint32_t stage, float frequency, float q = 1.0) { | |||
| int coef[5]; | |||
| double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); | |||
| double sinW0 = sin(w0); | |||
| double alpha = sinW0 / ((double)q * 2.0); | |||
| double cosW0 = cos(w0); | |||
| double scale = 1073741824.0 / (1.0 + alpha); | |||
| /* b0 */ coef[0] = scale; | |||
| /* b1 */ coef[1] = (-2.0 * cosW0) * scale; | |||
| /* b2 */ coef[2] = coef[0]; | |||
| /* a1 */ coef[3] = (-2.0 * cosW0) * scale; | |||
| /* a2 */ coef[4] = (1.0 - alpha) * scale; | |||
| setCoefficients(stage, coef); | |||
| } | |||
| private: | |||
| int *definition; | |||
| int32_t definition[32]; // up to 4 cascaded biquads | |||
| audio_block_t *inputQueueArray[1]; | |||
| }; | |||
| @@ -1378,24 +1378,52 @@ The actual packets are taken | |||
| </script> | |||
| <script type="text/x-red" data-help-name="AudioFilterBiquad"> | |||
| <h3>Summary</h3> | |||
| <p>description</p> | |||
| <div> | |||
| <p>Biquadratic cascaded filter, useful for all sorts of filtering. | |||
| Up to 4 stages may be cascaded. | |||
| </p> | |||
| <p align=center><img src="biquad.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>setLowpass</span>(stage, frequency, Q);</p> | |||
| <p class=desc>Configure one stage of the filter (0 to 3) with low pass | |||
| response, with the specified corner frequency and Q shape. If Q is | |||
| higher that 0.7071, be careful of filter gain (see below). | |||
| </p> | |||
| <p class=func><span class=keyword>setLowpass</span>(stage, frequency, Q);</p> | |||
| <p class=desc>Configure one stage of the filter (0 to 3) with high pass | |||
| response, with the specified corner frequency and Q shape. If Q is | |||
| higher that 0.7071, be careful of filter gain (see below). | |||
| </p> | |||
| <p class=func><span class=keyword>setLowpass</span>(stage, frequency, Q);</p> | |||
| <p class=desc>Configure one stage of the filter (0 to 3) with band pass | |||
| response. The filter has unity gain at the specified frequency. Q | |||
| controls the width of frequencies allowed to pass. | |||
| </p> | |||
| <p class=func><span class=keyword>setLowpass</span>(stage, frequency, Q);</p> | |||
| <p class=desc>Configure one stage of the filter (0 to 3) with band reject (notch) | |||
| response. Q controls the width of rejected frequencies. | |||
| </p> | |||
| <p class=func><span class=keyword>setCoefficients</span>(stage, array[5]);</p> | |||
| <p class=desc>Configure one stage of the filter (0 to 3) with an arbitrary | |||
| filter response. The array of coefficients is in order: B0, B1, B2, A1, A2. | |||
| Each coefficient must be less than 2.0 and greater than -2.0. The array | |||
| should be type double. Alternately, it may be type int, where 1.0 is | |||
| represented with 1073741824 (2<sup>30</sup>). | |||
| </p> | |||
| <h3>Notes</h3> | |||
| <p></p> | |||
| <p>Filters can with gain must have their input signals attenuated, so the | |||
| signal does not exceed 1.0. | |||
| </p> | |||
| <p>This object implements up to 4 cascaded stages. Unconfigured stages will | |||
| not pass any signal. | |||
| </p> | |||
| </script> | |||
| <script type="text/x-red" data-template-name="AudioFilterBiquad"> | |||
| <div class="form-row"> | |||
| @@ -46,6 +46,11 @@ noteOff KEYWORD2 | |||
| stop KEYWORD2 | |||
| play KEYWORD2 | |||
| updateCoefs KEYWORD2 | |||
| setCoefficients KEYWORD2 | |||
| setLowpass KEYWORD2 | |||
| setHighpass KEYWORD2 | |||
| setBandpass KEYWORD2 | |||
| setNotch KEYWORD2 | |||
| frequency KEYWORD2 | |||
| phase KEYWORD2 | |||
| amplitude KEYWORD2 | |||