I haven't tested everything I've done to my copy of Audio.cpp & Audio.h yet, and I haven't written a decent set of examples either, so I am not rushing to make a pull request but if anybody wants to take a peek at my initial additions/mods for the library they can look at my (albeit, I expect, temporary) fork at https://github.com/robsoles/Audio
Only testing by listening to headphones with an appropriate source on Line-in, the following sketch (compiled against my copy of the library) appears to meet my expectations - I am about to be dragged, kicking and screaming, roughly 1600 kilometres from anything I could use to more thoroughly test (and write sketches to test) my stuff for about three days so I figure I'll let anyone interested try this stuff sooner rather than later.
Anything I've added to my copy of the library that isn't used in that sketch should be considered untested.
If you try the above sketch I want your feedback please. If you try any of the 'untested' stuff in my fork I want your feedback twice as much
I will write tests/examples for the other bits and pieces as soon as practical for me. When opportunity allows I am going to use some audio sweeping equipment at my work to more thoroughly test filters and other details.
Only testing by listening to headphones with an appropriate source on Line-in, the following sketch (compiled against my copy of the library) appears to meet my expectations - I am about to be dragged, kicking and screaming, roughly 1600 kilometres from anything I could use to more thoroughly test (and write sketches to test) my stuff for about three days so I figure I'll let anyone interested try this stuff sooner rather than later.
Code:
// filterCalc test sketch..
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
//For Filter Type: 0 = LPF, 1 = HPF, 2 = BPF, 3 = NOTCH, 4 = PeakingEQ, 5 = LowShelf, 6 = HighShelf
#define FILTER_LOPASS 0
#define FILTER_HIPASS 1
#define FILTER_BANDPASS 2
#define FILTER_NOTCH 3
#define FILTER_PARAEQ 4
#define FILTER_LOSHELF 5
#define FILTER_HISHELF 6
/*
typedef union
{
// uint32_t uint1;
unsigned long uint1;
// int32_t int1;
long int1;
} transInt;
*/
const int myInput = AUDIO_INPUT_LINEIN;
// const int myInput = AUDIO_INPUT_MIC;
// each filter requires a set up parameters
int BassFilterParameters[] = { // notch, Fc=2175 Hz
896456695, -1707514503, 896456695, 1707514503, -719171567, 0, 0, 0};
int TrebFilterParameters[]={0,0,0,0,0};
int updateFilter[5];
// 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 BassFilter(BassFilterParameters);
AudioOutputI2S audioOutput; // audio shield: headphones & line-out
// Create Audio connections between the components
//
AudioConnection c1(audioInput, 0, audioOutput, 0);
AudioConnection c2(audioInput, 0, BassFilter, 0);
AudioConnection c3(BassFilter, 0, audioOutput, 1);
// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;
void filterCalc(uint8_t filtertype, float fC, float dB_Gain, float Q, uint32_t quantization_unit, uint32_t fS, int *coef)
{
// I used resources like http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
// to make this routine, I tested most of the filter types and they worked. Such filters have limits and
// before calling this routine with varying values the end user should check that those values are limited
// to valid results.
float A;
if(filtertype<FILTER_PARAEQ) A=pow(10,dB_Gain/20); else A=pow(10,dB_Gain/40);
float W0 = 2*3.14159265358979323846*fC/fS;
float cosw=cos(W0);
float sinw=sin(W0);
//float alpha = sinw*sinh((log(2)/2)*BW*W0/sinw);
//float beta = sqrt(2*A);
float alpha = sinw / (2 * Q);
float beta = sqrt(A)/Q;
float b0,b1,b2,a0,a1,a2;
switch(filtertype) {
case FILTER_LOPASS:
b0 = (1.0F - cosw) * 0.5F; // =(1-COS($H$2))/2
b1 = 1.0F - cosw;
b2 = (1.0F - cosw) * 0.5F;
a0 = 1.0F + alpha;
a1 = 2.0F * cosw;
a2 = alpha - 1.0F;
break;
case FILTER_HIPASS:
b0 = (1.0F + cosw) * 0.5F;
b1 = -(cosw + 1.0F);
b2 = (1.0F + cosw) * 0.5F;
a0 = 1.0F + alpha;
a1 = 2.0F * cosw;
a2 = alpha - 1.0F;
break;
case FILTER_BANDPASS:
b0 = alpha;
b1 = 0.0F;
b2 = -alpha;
a0 = 1.0F + alpha;
a1 = 2.0F * cosw;
a2 = alpha - 1.0F;
break;
case FILTER_NOTCH:
b0=1;
b1=-2*cosw;
b2=1;
a0=1+alpha;
a1=2*cosw;
a2=-(1-alpha);
break;
case FILTER_PARAEQ:
b0 = 1 + (alpha*A);
b1 =-2 * cosw;
b2 = 1 - (alpha*A);
a0 = 1 + (alpha/A);
a1 = 2 * cosw;
a2 =-(1-(alpha/A));
break;
case FILTER_LOSHELF:
b0 = A * ((A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw));
b1 = 2.0F * A * ((A-1.0F) - ((A+1.0F)*cosw));
b2 = A * ((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
a0 = (A+1.0F) + ((A-1.0F)*cosw) + (beta*sinw);
a1 = 2.0F * ((A-1.0F) + ((A+1.0F)*cosw));
a2 = -((A+1.0F) + ((A-1.0F)*cosw) - (beta*sinw));
break;
case FILTER_HISHELF:
b0 = A * ((A+1.0F) + ((A-1.0F)*cosw) + (beta*sinw));
b1 = -2.0F * A * ((A-1.0F) + ((A+1.0F)*cosw));
b2 = A * ((A+1.0F) + ((A-1.0F)*cosw) - (beta*sinw));
a0 = (A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw);
a1 = -2.0F * ((A-1.0F) - ((A+1.0F)*cosw));
a2 = -((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
}
a0=(a0*2)/(float)quantization_unit; // once here instead of five times there...
b0/=a0;
*coef++=(int)(b0+0.499);
b1/=a0;
*coef++=(int)(b1+0.499);
b2/=a0;
*coef++=(int)(b2+0.499);
a1/=a0;
*coef++=(int)(a1+0.499);
a2/=a0;
*coef++=(int)(a2+0.499);
}
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.route(1,1); // using I2S, using DAP
audioShield.dap_audio_eq(1); // using Biquad filters
audioShield.dap_peqs(1); // only using one of the filters.
filterCalc(FILTER_PARAEQ, 3300, 0, 0.2, 524288, 44100,TrebFilterParameters);
audioShield.load_peq(0,TrebFilterParameters);
audioShield.volume(60);
}
elapsedMillis chgMsec=0;
float lastbass=0;
float lastvol=0;
void loop() {
// every 10 ms, check for adjustment the tone & vol
if (chgMsec > 10) { // more regular updates for actual changes seems better.
float bass1 = analogRead(16);
bass1=((bass1-512)/512)*60;
bass1=(int)bass1;
bass1=bass1/10; // only changing for 0.1dB steps
if(lastbass!=bass1)
{
filterCalc(FILTER_PARAEQ, 220, bass1, 0.2, 2147483648, 44100, updateFilter);
filterCalc(FILTER_PARAEQ, 3300, -bass1, 0.2, 524288, 44100, TrebFilterParameters);
BassFilter.updateCoefs(updateFilter); // load for AudioBiquadFilter
audioShield.load_peq(0,TrebFilterParameters); // load for SGTL5000 PEQ.
lastbass=bass1;
}
float vol1=analogRead(15)/1.024;
vol1=(int)vol1;
vol1=vol1/10;
if(lastvol!=vol1)
{
audioShield.volume(vol1);
lastvol=vol1;
}
chgMsec = 0;
}
}
If you try the above sketch I want your feedback please. If you try any of the 'untested' stuff in my fork I want your feedback twice as much
I will write tests/examples for the other bits and pieces as soon as practical for me. When opportunity allows I am going to use some audio sweeping equipment at my work to more thoroughly test filters and other details.