#include <SPI.h> | #include <SPI.h> | ||||
#include <SD.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() { | void setup() { | ||||
// Audio connections require memory to work. For more | |||||
// detailed information, see the MemoryAndCpuUsage example | |||||
AudioMemory(12); | 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() { | void loop() { | ||||
// every 50 ms, adjust the volume | |||||
if (volmsec > 50) { | |||||
float vol = analogRead(15); | |||||
vol = vol / 1024; | |||||
audioShield.volume(vol); | |||||
volmsec = 0; | |||||
} | |||||
} | } | ||||
void AudioFilterBiquad::update(void) | void AudioFilterBiquad::update(void) | ||||
{ | { | ||||
audio_block_t *block; | 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; | uint32_t *data, *end; | ||||
int32_t *state; | int32_t *state; | ||||
block = receiveWritable(); | block = receiveWritable(); | ||||
end = (uint32_t *)(block->data) + AUDIO_BLOCK_SAMPLES/2; | end = (uint32_t *)(block->data) + AUDIO_BLOCK_SAMPLES/2; | ||||
state = (int32_t *)definition; | state = (int32_t *)definition; | ||||
do { | do { | ||||
a0 = *state++; | |||||
a1 = *state++; | |||||
a2 = *state++; | |||||
b0 = *state++; | |||||
b1 = *state++; | b1 = *state++; | ||||
b2 = *state++; | b2 = *state++; | ||||
aprev = *state++; | |||||
a1 = *state++; | |||||
a2 = *state++; | |||||
bprev = *state++; | bprev = *state++; | ||||
aprev = *state++; | |||||
sum = *state & 0x3FFF; | sum = *state & 0x3FFF; | ||||
data = end - AUDIO_BLOCK_SAMPLES/2; | data = end - AUDIO_BLOCK_SAMPLES/2; | ||||
do { | do { | ||||
in2 = *data; | 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_32x16t(sum, b1, bprev); | ||||
sum = signed_multiply_accumulate_32x16b(sum, b2, 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 &= 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); | 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; | sum &= 0x3FFF; | ||||
aprev = in2; | |||||
*data++ = bprev; | |||||
bprev = in2; | |||||
*data++ = aprev; | |||||
} while (data < end); | } while (data < end); | ||||
flag = *state & 0x80000000; | flag = *state & 0x80000000; | ||||
*state++ = sum | flag; | *state++ = sum | flag; | ||||
*(state-2) = bprev; | |||||
*(state-3) = aprev; | |||||
*(state-2) = aprev; | |||||
*(state-3) = bprev; | |||||
} while (flag); | } while (flag); | ||||
transmit(block); | transmit(block); | ||||
release(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(); | __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(); | __enable_irq(); | ||||
} | } | ||||
class AudioFilterBiquad : public AudioStream | class AudioFilterBiquad : public AudioStream | ||||
{ | { | ||||
public: | 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); | 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: | private: | ||||
int *definition; | |||||
int32_t definition[32]; // up to 4 cascaded biquads | |||||
audio_block_t *inputQueueArray[1]; | audio_block_t *inputQueueArray[1]; | ||||
}; | }; | ||||
</script> | </script> | ||||
<script type="text/x-red" data-help-name="AudioFilterBiquad"> | <script type="text/x-red" data-help-name="AudioFilterBiquad"> | ||||
<h3>Summary</h3> | <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> | <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>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> | </p> | ||||
<h3>Notes</h3> | <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> | ||||
<script type="text/x-red" data-template-name="AudioFilterBiquad"> | <script type="text/x-red" data-template-name="AudioFilterBiquad"> | ||||
<div class="form-row"> | <div class="form-row"> |
stop KEYWORD2 | stop KEYWORD2 | ||||
play KEYWORD2 | play KEYWORD2 | ||||
updateCoefs KEYWORD2 | updateCoefs KEYWORD2 | ||||
setCoefficients KEYWORD2 | |||||
setLowpass KEYWORD2 | |||||
setHighpass KEYWORD2 | |||||
setBandpass KEYWORD2 | |||||
setNotch KEYWORD2 | |||||
frequency KEYWORD2 | frequency KEYWORD2 | ||||
phase KEYWORD2 | phase KEYWORD2 | ||||
amplitude KEYWORD2 | amplitude KEYWORD2 |