/* Audio Sweeper
Objective: Output a sinusoidal waveform from 10Hz to ~22500Hz read back energy value to determine external attenuation using as few extra parts, aside from Teensy, to do so.
*/
typedef struct
{
char data[256];
uint8_t ptr=0;
} byte_buffer;
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
const char cmds[]="gain:\0filters:\0filter:\0sweepid\0\0"; // channel, filter_number (0-3), cF, Gain, Q
/*
gain:#channel,#input,#value ; channel 0=left, 1=right, 2=dac ; input 0=filtered, 1=synthSine, 2 table ; value (float) gain setting.
gain:#channel,2,#element,#value ; element 0 to 275 ; value (float) gain setting.
filters:#channel,#number_of_filters
filter:#channel,#filter_num,#filter_type,#frequency,#gain,#Q
*/
byte_buffer cons;
#define START_FREQ 1.0111248868808
// #define START_FREQ 10
#define LIMIT_FREQ AUDIO_SAMPLE_RATE_EXACT/2
#define MULTI_FREQ 1.02608176245271
#define SETTLE_TIME 10
#define SETTLE_TIMEOUT 25
// roughly 900mV PP.
#define BASE_AMPLITUDE (0.9/3.3)
int L_filters[32]; // not immediately using them all but can see scope for 4 sets.
int R_filters[32]; // for use in preamps with three inputs, filters might be used
int A_filters[32]; // to pre-attenuate to match known responses from pickups.
uint16_t Gain_L[276]; // these are inappropriate unless START_FREQ equ 10
uint16_t Gain_R[276]; // when executing this sketch as is I never sent it
uint16_t Gain_A[276]; // any gain changing commands although some are safe.
// Anything to do with 'gain tables' will cause trouble till these are resized.
int tFilter[5]; // temporary coefficient holder
uint8_t Gain_F=0; // all three outputs can use gain tables instead of flat or filtered, defaulting to flat here.
AudioInputI2S stereoIn;
AudioMixer4 mixer_P; // to temper Stereo_R
AudioPeak peakIn[2];
AudioSynthWaveform synthSine;
AudioFilterBiquad filters_L(L_filters);
AudioFilterBiquad filters_R(R_filters);
AudioFilterBiquad filters_A(A_filters);
AudioMixer4 mixer_L;
AudioMixer4 mixer_R;
AudioMixer4 mixer_A;
AudioOutputI2S stereoOut;
AudioOutputAnalog monoOut;
AudioPeak peakOut[4];
AudioConnection cIn_L(stereoIn,0,peakIn[0],0);
AudioConnection cIn_P(stereoIn,1,mixer_P,0);
AudioConnection cIn_R(mixer_P,0,peakIn[1],0);
AudioConnection cFlt_L(synthSine,0,filters_L,0);
AudioConnection cFlt_R(synthSine,0,filters_R,0);
AudioConnection cFlt_A(synthSine,0,filters_A,0);
AudioConnection cPkRaw(synthSine,0,peakOut[3],0);
AudioConnection cMix_L(filters_L,0,mixer_L,0);
AudioConnection cMix_Ls(synthSine,0,mixer_L,1);
AudioConnection cMix_R(filters_R,0,mixer_R,0);
AudioConnection cMix_Rs(synthSine,0,mixer_R,1);
AudioConnection cMix_A(filters_A,0,mixer_A,0);
AudioConnection cMix_As(synthSine,0,mixer_A,1);
AudioConnection cOut_L(mixer_L,0,stereoOut,0);
AudioConnection cPeak_L(mixer_L,0,peakOut[0],0);
AudioConnection cOut_R(mixer_R,0,stereoOut,1);
AudioConnection cPeak_R(mixer_R,0,peakOut[1],0);
AudioConnection cOut_A(mixer_A,0,monoOut,0);
AudioConnection cPeak_A(mixer_A,0,peakOut[2],0);
float freq=START_FREQ;
boolean op_running=false;
AudioControlSGTL5000 audioShield;
uint16_t steps=0;
float strHex(char* buf, unsigned char* ptr)
{
unsigned char go=1;
unsigned char breaker=0;
float tmp=0;
while((*buf<48)&&(*buf!=0))
{
buf++;
*ptr+=1;
}
if((*buf=='0')&&(*(buf+1)=='x'))
{
buf+=2;
*ptr+=2;
}
if(*buf==0) return 0;
while(go!=0&&(++breaker)) {
if((*buf>64)&&(*buf<71)) *buf-=7;
if((*buf>96)&&(*buf<103)) *buf-=39;
if((*buf>47)&&(*buf<64)) {
tmp=(tmp*16)+((*buf)-48);
buf++;
*ptr+=1;
} else {
go=0;
}
}
return tmp;
}
float strfig(char* buf, unsigned char* ptr)
{
unsigned char go=1;
unsigned char breaker=0;
float tmp=0;
float frac=0;
float sign1=1;
while(go!=0&&(++breaker)) {
if(*buf!=0) {
if((*buf<33)||(*buf=='=')||(*buf==',')||(*buf==';')) {
buf++;
*ptr+=1;
} else {
go=0;
}
} else {
go=0;
}
}
if(((*buf==0)||(*buf<48)||(*buf>57))&&(*buf!='-')) {
return 0;
}
if(*buf=='-')
{
sign1=-1;
buf++;
*ptr+=1;
}
if((*buf=='0')&&(*(buf+1)=='x')) {
return sign1*strHex(buf,ptr);
}
go=1;
while(go!=0&&(++breaker)) {
if((*buf>47)&&(*buf<58)) {
if(frac)
{
tmp=tmp+(((*buf)-48)/frac);
frac*=10;
} else {
tmp=(tmp*10)+((*buf)-48);
}
buf++;
*ptr+=1;
} else if(*buf=='.') {
if(frac) return sign1*tmp; // second decimal point in a number is bullshit.
frac=10;
buf++;
*ptr+=1;
} else {
go=0;
}
}
return sign1*tmp;
}
unsigned char strfig(char* buf1, unsigned char* ptr, const char* cPtr)
{
const char * sPtr;
char * tPtr;
unsigned char count=0,cnt2,go=1;
while(go)
{
count++;
sPtr=cPtr;
while(*cPtr!=0) {
cPtr++;
}
cPtr++;
tPtr=buf1;
cnt2=0;
while((*sPtr==*tPtr)&&(*tPtr!=0))
{
cnt2++;
sPtr++;
tPtr++;
}
if(*sPtr==0)
{
*ptr+=cnt2;
return count;
}
go=*cPtr;
}
return 0;
}
void setup ()
{
AudioMemory(10);
op_running=synthSine.begin(BASE_AMPLITUDE,(unsigned short)freq,TONE_TYPE_SINE);
calcBiquad(FILTER_PARAEQ,1000,0,0.707,2147483648,AUDIO_SAMPLE_RATE_EXACT,tFilter);
filters_L.updateCoefs(tFilter);
filters_R.updateCoefs(tFilter);
filters_A.updateCoefs(tFilter);
audioShield.enable();
audioShield.inputSelect(AUDIO_INPUT_LINEIN);
audioShield.adc_hpf(1); // not done for the table I posted previously...
audioShield.volume(0);
audioShield.dac_vol(97);
audioShield.unmuteLineout();
Serial.begin(Serial.baud());
// delay(5000);
// Serial.println("Ready or not!");
for(uint16_t index=0;index<276;index++) Gain_L[index]=Gain_R[index]=Gain_A[index]=32767; // 1.
mixer_P.gain(0,1);
mixer_L.gain(0,0);
mixer_R.gain(0,0);
mixer_A.gain(0,0); // synthSine filtered
mixer_L.gain(1,1);
mixer_R.gain(1,1);
mixer_A.gain(1,1); // synthSine flat
}
elapsedMillis capture_time;
#if PIN_VOL!=0
elapsedMillis volUpd;
#endif
float lastVol=BASE_AMPLITUDE;
float bp[]={10,10};
int timed1=0;
// uint16_t steps=0;
uint8_t chan1;
uint8_t inpt1;
float gain1;
void loop()
{
if(Serial.available())
{
char chr=Serial.read();
if(chr<32)
{
cons.data[cons.ptr]=0;
cons.ptr=0;
while(cons.data[cons.ptr]!=0)
{
switch(strfig(&cons.data[cons.ptr],&cons.ptr,cmds))
{
case 1: // gain=#channel,#input,#float_gain
chan1=strfig(&cons.data[cons.ptr],&cons.ptr);
inpt1=strfig(&cons.data[cons.ptr],&cons.ptr);
switch(chan1)
{
case 0: // mixer_L
if(inpt1<2)
{
mixer_L.gain(inpt1,strfig(&cons.data[cons.ptr],&cons.ptr));
Gain_F&=254;
} else {
Gain_L[(uint16_t)strfig(&cons.data[cons.ptr],&cons.ptr)]=strfig(&cons.data[cons.ptr],&cons.ptr)*32767;
Gain_F|=1;
}
break;
case 1: // mixer_R
if(inpt1<2)
{
mixer_R.gain(inpt1,strfig(&cons.data[cons.ptr],&cons.ptr));
Gain_F&=253;
} else {
Gain_R[(uint16_t)strfig(&cons.data[cons.ptr],&cons.ptr)]=strfig(&cons.data[cons.ptr],&cons.ptr)*32767;
Gain_F|=2;
}
break;
case 2: // mixer_A
if(inpt1<2)
{
mixer_A.gain(inpt1,strfig(&cons.data[cons.ptr],&cons.ptr));
Gain_F&=251;
} else {
Gain_A[(uint16_t)strfig(&cons.data[cons.ptr],&cons.ptr)]=strfig(&cons.data[cons.ptr],&cons.ptr)*32767;
Gain_F|=4;
}
break;
case 3: // synthSine
lastVol=strfig(&cons.data[cons.ptr],&cons.ptr);
synthSine.amplitude(lastVol);
break;
case 4: // mixer_P
mixer_P.gain(0,strfig(&cons.data[cons.ptr],&cons.ptr));
}
break;
case 2: // filters=#channel,#number_of_filters
chan1=strfig(&cons.data[cons.ptr],&cons.ptr);
inpt1=strfig(&cons.data[cons.ptr],&cons.ptr);
if(!inpt1) calcBiquad(FILTER_PARAEQ,1000,0,0.707,2147483648,AUDIO_SAMPLE_RATE_EXACT,tFilter); // a flat, no effect, filter
switch(chan1)
{
case 0: // filter_L; L_filters[]....
if(inpt1>3) L_filters[23]|=0x80000000; else L_filters[23]&=0x3FFF;
if(inpt1>2) L_filters[15]|=0x80000000; else L_filters[15]&=0x3FFF;
if(inpt1>1) L_filters[7]|=0x80000000; else L_filters[7]&=0x3FFF;
if(!inpt1) filters_L.updateCoefs(tFilter);
break;
case 1: // filter_R
if(inpt1>3) R_filters[23]|=0x80000000; else R_filters[23]&=0x3FFF;
if(inpt1>2) R_filters[15]|=0x80000000; else R_filters[15]&=0x3FFF;
if(inpt1>1) R_filters[7]|=0x80000000; else R_filters[7]&=0x3FFF;
if(!inpt1) filters_R.updateCoefs(tFilter);
break;
case 2: // filter_A
if(inpt1>3) A_filters[23]|=0x80000000; else A_filters[23]&=0x3FFF;
if(inpt1>2) A_filters[15]|=0x80000000; else A_filters[15]&=0x3FFF;
if(inpt1>1) A_filters[7]|=0x80000000; else A_filters[7]&=0x3FFF;
if(!inpt1) filters_A.updateCoefs(tFilter);
}
break;
case 3: // filter=#channel,#filter_num,#filter_type,#frequency,#gain,#Q
chan1=strfig(&cons.data[cons.ptr],&cons.ptr);
inpt1=strfig(&cons.data[cons.ptr],&cons.ptr);
calcBiquad(strfig(&cons.data[cons.ptr],&cons.ptr),strfig(&cons.data[cons.ptr],&cons.ptr),strfig(&cons.data[cons.ptr],&cons.ptr),strfig(&cons.data[cons.ptr],&cons.ptr),2147483648,AUDIO_SAMPLE_RATE_EXACT,tFilter);
switch(chan1)
{
case 0: // filter_L; L_filters[]....
filters_L.updateCoefs(inpt1,tFilter);
break;
case 1: // filter_R
filters_R.updateCoefs(inpt1,tFilter);
break;
case 2: // filter_A
filters_A.updateCoefs(inpt1,tFilter);
}
break;
case 4:
Serial.println("sweeper");
break;
default:
if(cons.data[cons.ptr]!=0) cons.ptr++;
}
}
cons.ptr=0;
} else {
cons.data[cons.ptr++]=chr;
}
}
if(capture_time>timed1)
{ // the objects have had time to have their effects.
peakIn[0].stop();
peakIn[1].stop();
peakOut[0].stop();
peakOut[1].stop();
peakOut[2].stop();
peakOut[3].stop();
Serial.printf("%u,%u,%u,%u,%u,%u,%u,%u\n",steps,(unsigned short)freq,peakIn[0].Dpp(),peakIn[1].Dpp(),peakOut[0].Dpp(),peakOut[1].Dpp(),peakOut[2].Dpp(),peakOut[3].Dpp());
// Serial.printf("%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n",steps,(unsigned short)freq,peakIn[0].Dpp(),peakIn[1].Dpp(),peakOut[0].Dpp(),peakOut[1].Dpp(),peakOut[2].Dpp(),peakOut[3].Dpp(), Gain_L[steps],Gain_R[steps],Gain_A[steps]);
int temp1=(int)freq;
while((int)freq==temp1) freq*=MULTI_FREQ;
if(freq>LIMIT_FREQ)
{
freq=START_FREQ;
// Serial.printf("0Hz,0,%u\n",steps);
bp[0]=bp[1]=10;
steps=0;
} else {
steps++;
if((int)freq==22050) freq=22049;
}
if(Gain_F&1) mixer_L.gain(1,(float)Gain_L[steps]/32767);
if(Gain_F&2) mixer_R.gain(1,(float)Gain_R[steps]/32767);
if(Gain_F&4) mixer_A.gain(1,(float)Gain_A[steps]/32767);
if(op_running==false) // AudioSynthWaveform doesn't start output when .begin in setup() apparently.
{
op_running=synthSine.begin(lastVol,(unsigned short)freq,TONE_TYPE_SINE);
// Serial.printf("synthSine.begin(%f,%f,TONE_TYPE_SINE)\n",lastVol,freq);
} else {
synthSine.frequency((unsigned short)freq);
}
if(freq>bp[0])
{
// Serial.print(bp[0],DEC);
if(bp[0]==bp[1]*10)
{
// Serial.println("Hz,0,#");
bp[1]*=10;
bp[0]=bp[1]*2;
} else {
// Serial.println("Hz,0,|");
bp[0]+=bp[1];
}
}
float freq1=(int)freq;
if(freq1>0) timed1=(1/freq1)*1000; else timed1=1000;
if(freq>99&&freq1<125) timed1=9;
if(timed1<4) timed1=4;
timed1=(timed1*2)+1;
capture_time=0;
} else if(capture_time==3) {
peakIn[0].begin();
peakIn[1].begin();
peakOut[0].begin();
peakOut[1].begin();
peakOut[2].begin();
peakOut[3].begin();
}
}