New Guy Looking For Teensy DSP programming tips

Status
Not open for further replies.
Ive not looked the the USB audio code, its not a secret, but its probably quite complex low level DMA stuff.
I suspect the computer internally is converting to a common representation

I agree there's no clock-mismatch but there's no "common representation" for converting from 44117(for example) to 44100Hz. It would require an interpolation by 44100 and a decimation by 44117. Smells more like an asynchronous sample rate converter, a hardware device somewhere in the chain.
 
By common representation I meant something like 192kSPS/32bit, ie over-sampled and good bit depth. Computer audio
sub-systems are pretty complex these days and aren't short of hardware DSP grunt.
 
By common representation I meant something like 192kSPS/32bit, ie over-sampled and good bit depth. Computer audio
sub-systems are pretty complex these days and aren't short of hardware DSP grunt.

Interesting. 640/147. Alotta coefs. Maybe the 44.1K is not converted exactly to 192K by using smaller ratios, then resolved later by the HW? Interesting subjects (at least for me). Also, a high performance ASRC in software is possible but don't know if anyone ever developed one.
 
Well maybe not, but the only step that needs a lot of coefficients is the first oversampling, as the anti-aliasing requirement is
demanding - thereafter filter requirements are modest. Polyphase FIR techniques make it super efficient to resample by an integer ratio.

For instance if you want to convert by a ratio of 11/7 with a 121 coeff FIR filter you might naively expect 11*121 = 1331 muladds per
input sample, but actually you only need 121/7 = 17.3 muladds per input, 77 times less work than the naive approach.
 
IE arm_fir_interpolate_f32 and arm_fir_decimate_f32?

Doesn't seem to have a combined up-down FIR rate converter though, which is a shame as it is more general than either
of those function and much more efficient for converting by ratios than upsampling followed by downsampling.
 
Next problem in my Teensy adventure. Wanted to add an OLED 128x32 (Adafruit_SSD1306) display to my AGC project to monitor the left & right agcGain's. I found that the display blocks the execution of my loop for 50 msec every time I call it. If I'm processing a queue of 128 samples at 44.1K, I need to keep the loop cycle rate at 128/44100 or < 2.9 msec. As I only need to monitor the gains for the installation, I added a switch to call the display but it's not a very clean solution to glitch the audio every time I push the button. Any suggestions? What about multi-threading? Here's my latest code:

Code:
  #include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Bounce.h>
#define OLEDbutton 15
#define OLED_RESET 4
//SDA=A4
//SCL=A5
Adafruit_SSD1306 display(OLED_RESET);

//#define DEBUG
//#define PASSTHRU
//#define USE_SD

#ifdef USE_SD
AudioPlaySdWav            playSdWav1;
AudioOutputUSB            audioOutput;    //Puts digital samples to Audacity input (MME, digital audio interface) for recording to wav file
#else
AudioInputI2S             i2s1;           
#endif
AudioFilterFIR            fir1;           
AudioFilterFIR            fir2;           
AudioRecordQueue          queue3;         
AudioRecordQueue          queue1;         
AudioPlayQueue            queue2;         
AudioPlayQueue            queue4;         
AudioOutputI2S            i2s2;           
#ifdef USE_SD
AudioConnection           patchCord1(playSdWav1, 0, fir1, 0);
AudioConnection           patchCord2(playSdWav1, 1, fir2, 0);
#else
AudioConnection           patchCord1(i2s1, 0, fir1, 0);
AudioConnection           patchCord2(i2s1, 1, fir2, 0);
#endif
AudioConnection           patchCord3(fir1, queue1);
AudioConnection           patchCord4(fir2, queue3);
AudioConnection           patchCord5(queue2, 0, i2s2, 0);
AudioConnection           patchCord6(queue4, 0, i2s2, 1);
#ifdef USE_SD
AudioConnection           patchCord7(queue2, 0, audioOutput,0);
AudioConnection           patchCord8(queue4, 0, audioOutput,1);
#endif
AudioControlSGTL5000      sgtl5000_1;     

#ifdef USE_SD
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14
#else 
//const int myInput = AUDIO_INPUT_MIC;
const int myInput = AUDIO_INPUT_LINEIN;
#endif
//---------------------------------------------FIR parameters ---------------------
//#define FIRFILTER HIPASSFILTER
#define FIRFILTER FLATFILTER
#define NUM_COEFFS 50
double HIPASSFILTER[]=
{
#include "numHipass.h" //Arbitrary xfer function using fft for hpf fc=4000 fir filter design - (choice no. 13 of calcfilt.m)
};
double FLATFILTER[]=
{
#include "numflat.h" //Arbitrary xfer function using fft for flat fir filter design - (choice no. 14 of calcfilt.m)
};
double *coefs1 = FIRFILTER;
double *coefs2 = FIRFILTER;
short int COEFS1[NUM_COEFFS];
short int COEFS2[NUM_COEFFS];
//---------------------------------------------------------------------------------
int32_t agcGain1=0x08000000;//.5 in Q3.29 format
int32_t agcGain2=0x08000000;//.5 in Q3.29 format
int32_t ref=293504;//.07*AUDIO_BLOCK_SAMPLES in Q15 format(2293*128)
#ifdef DEBUG
uint32_t cyc1=0,cyc2=0;
#endif
Bounce button0 = Bounce(OLEDbutton, 15); //15msec debounce

void setup() {
  pinMode(OLEDbutton, INPUT_PULLUP); //push button to display current agcGain's on OLED display
  Serial.begin(115200);
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  displayOLED();
  display.display();  
  delay(2000);
  // allocate memory for the audio library
  AudioMemory(32);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
#ifdef USE_SD
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  delay(1000);
#else
  sgtl5000_1.inputSelect(myInput);
#endif 
  // Initialize the filter
  transformCoeffs( COEFS1, coefs1, NUM_COEFFS );
  transformCoeffs( COEFS2, coefs2, NUM_COEFFS );
  fir1.begin(COEFS1,NUM_COEFFS);
  fir2.begin(COEFS2,NUM_COEFFS);
  // Start the record queue
  queue1.begin();
  queue3.begin();
#ifdef DEBUG
  Serial.println("setup done");
#endif
}

// audio volume
int volume = 0;
unsigned long last_time = millis();

void loop()
{
#ifdef USE_SD
  if (playSdWav1.isPlaying() == false) 
  {
    Serial.println("Start playing");
    playSdWav1.play("Tower.wav");
//    playSdWav1.play("multisin.wav");
//    playSdWav1.play("noise.wav");
    delay(10);
  }
#endif
  button0.update();
//--------------------------- Left channel -----------------------------
  if (queue1.available() >= 1) //Left Channel
  {
#ifdef DEBUG
    cyc1 = ARM_DWT_CYCCNT;
    agcCalc(queue1.readBuffer(),queue2.getBuffer(),&agcGain1);
    cyc1 = ARM_DWT_CYCCNT - cyc1;
#else
    agcCalc(queue1.readBuffer(),queue2.getBuffer(),&agcGain1);
#endif
    // Free the input audio buffer
    queue1.freeBuffer();
    // and play it back into the audio queue
    queue2.playBuffer();
  } //if (queue1.available() >= 1)
//--------------------------- Right channel -----------------------------
  if (queue3.available() >= 1) 
  {
#ifdef DEBUG
    cyc2 = ARM_DWT_CYCCNT;
    agcCalc(queue3.readBuffer(),queue4.getBuffer(),&agcGain2);
    cyc2 = ARM_DWT_CYCCNT - cyc2;
#else
    agcCalc(queue3.readBuffer(),queue4.getBuffer(),&agcGain2);
#endif
    // Free the input audio buffer
    queue3.freeBuffer();
    // and play it back into the audio queue
    queue4.playBuffer();
  } //if (queue3.available() >= 1)
//--------------------------- Volume control -----------------------------
  int n = analogRead(0);
  if (n != volume) 
  {
    volume = n;
    sgtl5000_1.volume((float)n / 1023.);
    //uncomment this line if your audio shield has the volume pot
    //audioShield.volume((float)n / 1023);
  }  
//-------- oled display updated only if button0 is activated 'cuz OLED is too slow(50msec & audio queue is updated every 3msec) so causes a glitch. -------------
  if (button0.fallingEdge())
  {
    displayOLED();
    display.display();    
  }
  #ifdef DEBUG  
  // print information about resource usage
  // Proc = 18 (18),  Mem = 4 (5)
  if (millis() - last_time >= 2500) 
  {
    Serial.print("Proc = ");
    Serial.print(AudioProcessorUsage());
    Serial.print(" % ");
    Serial.print(" (");    
    Serial.print(AudioProcessorUsageMax());
    Serial.print(" %),  Mem = ");
    Serial.print(AudioMemoryUsage());
    Serial.print(" (");    
    Serial.print(AudioMemoryUsageMax());
    Serial.println(")");
    Serial.print("agc1: Gain=");
    Serial.print((float)agcGain1/(float)0x10000000);
    Serial.print(" cycles/sample=");
    Serial.println((float)cyc1/(float)AUDIO_BLOCK_SAMPLES);
    Serial.print("agc2: Gain=");
    Serial.print((float)agcGain2/(float)0x10000000);
    Serial.print(" cycles/sample=");
    Serial.println((float)cyc2/(float)AUDIO_BLOCK_SAMPLES);
    Serial.println("");
    last_time = millis();
  }
#endif
}

void transformCoeffs( short int out[], double in[], int k )
{
  for ( int i = 0 ; i < k ; i++ )
    out[i] = 32767 * in[i];
}

void agcCalc(int16_t *inbuf,int16_t *outbuf,int32_t *agcGain)
{
  int16_t *bp1=inbuf,*bp2=outbuf;
#ifdef PASSTHRU
    // Copy from input to output buffer
    for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) 
    {
      *bp2++ = *bp1++;
    }
#else
    // Apply agcGain on input with saturation to output buffer
    int32_t sum=0,sum1=0,sum2=0,sum3=0,res,res1,res2,res3,err;
    for(int i = 0;i < AUDIO_BLOCK_SAMPLES>>2;i++) 
    { 
      //ref. dspinst.h in Teensy audio library
      res=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res1=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res2=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res3=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      *bp2++=saturate16(res);
      *bp2++=saturate16(res1);
      *bp2++=saturate16(res2);
      *bp2++=saturate16(res3);
      sum+=abs(res);
      sum1+=abs(res1);
      sum2+=abs(res2);
      sum3+=abs(res3);
    }
    sum+=(sum1+sum2+sum3);
    // Calculate new agcGain for next input buffer
    err=ref-sum; 
    *agcGain+=err>>3; //update gain 
    *agcGain=min(*agcGain,0x7ff80000); //keep agcGain < 8.0
    *agcGain=max(*agcGain,0); //keep agcGain >= 0
#endif
}
void displayOLED(){
  display.clearDisplay();
  display.setCursor(0,0);
  display.print("agcGains: ");
  display.print((float)agcGain1/(float)0x10000000);
  display.print(","); 
  display.print((float)agcGain2/(float)0x10000000);
}
 
I2C is very slow (for a bitmapped display), try the SPI interface, perhaps try 8MHz in the first instance and when that works try faster.
 
I2C is very slow (for a bitmapped display), try the SPI interface, perhaps try 8MHz in the first instance and when that works try faster.

I guess I chose the wrong option when I ordered it. Anyhow, seems it will be difficult to go from 50 msec to < 2.9 msec. I'll check it out. Thanx.
 
128x32 pixels is at most 12288 bytes for an RGB bitmap which is a few ms at even 8MHz SPI.
Its worth looking to see if there's a DMA version of a library that drives I2C - that would free the processor.
I've no idea if there is one for this display.
 
128x32 pixels is at most 12288 bytes for an RGB bitmap which is a few ms at even 8MHz SPI.
Its worth looking to see if there's a DMA version of a library that drives I2C - that would free the processor.
I've no idea if there is one for this display.

Thank you. I will look at that option.
 
128x32 pixels is at most 12288 bytes for an RGB bitmap which is a few ms at even 8MHz SPI.
Its worth looking to see if there's a DMA version of a library that drives I2C - that would free the processor.
I've no idea if there is one for this display.

At my level, didn't find anything so just added an ATMega328P 3.3V, 8MHz on the Teensy TX4 pin to manage the OLED. It's overkill but doesn't take much additional space and cheap.

TVagcProject.jpg
 
I have developed a "final" version for my (Teensy+AudioAdapter+Oled) TV & Stereo agc box. It works OK but I noticed a slight 50Hz humming noise added to the sound. I am supposing it's ground loop noise. I have ordered a ground loop eliminator. Any suggestions? Thanks.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>


//#define DEBUG
//#define PASSTHRU
//#define USE_SD

#ifdef USE_SD
AudioPlaySdWav            playSdWav1;
AudioOutputUSB            audioOutput;    //Puts digital samples to Audacity input (MME, digital audio interface) for recording to wav file
#else
AudioInputI2S             i2s1;           
#endif
AudioFilterFIR            fir1;           
AudioFilterFIR            fir2;           
AudioRecordQueue          queue3;         
AudioRecordQueue          queue1;         
AudioPlayQueue            queue2;         
AudioPlayQueue            queue4;         
AudioOutputI2S            i2s2;           
#ifdef USE_SD
AudioConnection           patchCord1(playSdWav1, 0, fir1, 0);
AudioConnection           patchCord2(playSdWav1, 1, fir2, 0);
#else
AudioConnection           patchCord1(i2s1, 0, fir1, 0);
AudioConnection           patchCord2(i2s1, 1, fir2, 0);
#endif
AudioConnection           patchCord3(fir1, queue1);
AudioConnection           patchCord4(fir2, queue3);
AudioConnection           patchCord5(queue2, 0, i2s2, 0);
AudioConnection           patchCord6(queue4, 0, i2s2, 1);
#ifdef USE_SD
AudioConnection           patchCord7(queue2, 0, audioOutput,0);
AudioConnection           patchCord8(queue4, 0, audioOutput,1);
#endif
AudioControlSGTL5000      sgtl5000_1;     

#ifdef USE_SD
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14
#else 
//const int myInput = AUDIO_INPUT_MIC;
const int myInput = AUDIO_INPUT_LINEIN;
#endif
//---------------------------------------------FIR parameters ---------------------
//#define FIRFILTER HIPASSFILTER
#define FIRFILTER FLATFILTER
#define NUM_COEFFS 50
double HIPASSFILTER[]=
{
#include "numHipass.h" //Arbitrary xfer function using fft for hpf fc=4000 fir filter design - (choice no. 13 of calcfilt.m)
};
double FLATFILTER[]=
{
#include "numflat.h" //Arbitrary xfer function using fft for flat fir filter design - (choice no. 14 of calcfilt.m)
};
double *coefs1 = FIRFILTER;
double *coefs2 = FIRFILTER;
short int COEFS1[NUM_COEFFS];
short int COEFS2[NUM_COEFFS];
//---------------------------------------------------------------------------------
int32_t agcGain1=0x08000000;//.5 in Q3.29 format
int32_t agcGain2=0x08000000;//.5 in Q3.29 format
int32_t ref=293504;//.07*AUDIO_BLOCK_SAMPLES in Q15 format(2293*128)
#ifdef DEBUG
uint32_t cyc1=0,cyc2=0;
#endif


void setup() {

  Serial.begin(115200);
  Serial4.begin(9600);
// allocate memory for the audio library
  AudioMemory(32);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);
  sgtl5000_1.lineInLevel(0); //max 3.12Vp-p instead of (5)1.33
#ifdef USE_SD
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  delay(1000);
#else
  sgtl5000_1.inputSelect(myInput);
#endif 
  // Initialize the filter
  transformCoeffs( COEFS1, coefs1, NUM_COEFFS );
  transformCoeffs( COEFS2, coefs2, NUM_COEFFS );
  fir1.begin(COEFS1,NUM_COEFFS);
  fir2.begin(COEFS2,NUM_COEFFS);
  // Start the record queue
  queue1.begin();
  queue3.begin();
#ifdef DEBUG
  Serial.println("setup done");
#endif
}

// audio volume
int volume = 0;
unsigned long last_time = millis();
unsigned long last_time1 = millis();

void loop()
{
#ifdef USE_SD
  if (playSdWav1.isPlaying() == false) 
  {
    Serial.println("Start playing");
    playSdWav1.play("Tower.wav");
//    playSdWav1.play("multisin.wav");
//    playSdWav1.play("noise.wav");
    delay(10);
  }
#endif

//--------------------------- Left channel -----------------------------
  if (queue1.available() >= 1) //Left Channel
  {
#ifdef DEBUG
    cyc1 = ARM_DWT_CYCCNT;
    agcCalc(queue1.readBuffer(),queue2.getBuffer(),&agcGain1);
    cyc1 = ARM_DWT_CYCCNT - cyc1;
#else
    agcCalc(queue1.readBuffer(),queue2.getBuffer(),&agcGain1);
#endif
    // Free the input audio buffer
    queue1.freeBuffer();
    // and play it back into the audio queue
    queue2.playBuffer();
  } //if (queue1.available() >= 1)
//--------------------------- Right channel -----------------------------
  if (queue3.available() >= 1) 
  {
#ifdef DEBUG
    cyc2 = ARM_DWT_CYCCNT;
    agcCalc(queue3.readBuffer(),queue4.getBuffer(),&agcGain2);
    cyc2 = ARM_DWT_CYCCNT - cyc2;
#else
    agcCalc(queue3.readBuffer(),queue4.getBuffer(),&agcGain2);
#endif
    // Free the input audio buffer
    queue3.freeBuffer();
    // and play it back into the audio queue
    queue4.playBuffer();
  } //if (queue3.available() >= 1)
//--------------------------- Volume control -----------------------------
  int n = analogRead(0);
  if (n != volume) 
  {
    volume = n;
    sgtl5000_1.volume((float)n / 1023.);
    //uncomment this line if your audio shield has the volume pot
    //audioShield.volume((float)n / 1023);
  }  

  if (millis() - last_time1 >= 2500) //Send agcGains to OLED display controlled by Nano
  {
    Serial4.print("agcGains: ");
    Serial4.print((float)agcGain1/(float)0x10000000);
    Serial4.print(",");
    Serial4.println((float)agcGain2/(float)0x10000000);
    last_time1=millis();
  }
#ifdef DEBUG  
  // print information about resource usage
  // Proc = 18 (18),  Mem = 4 (5)
  if (millis() - last_time >= 2500) 
  {
    Serial.print("Proc = ");
    Serial.print(AudioProcessorUsage());
    Serial.print(" % ");
    Serial.print(" (");    
    Serial.print(AudioProcessorUsageMax());
    Serial.print(" %),  Mem = ");
    Serial.print(AudioMemoryUsage());
    Serial.print(" (");    
    Serial.print(AudioMemoryUsageMax());
    Serial.println(")");
    Serial.print("agc1: Gain=");
    Serial.print((float)agcGain1/(float)0x10000000);
    Serial.print(" cycles/sample=");
    Serial.println((float)cyc1/(float)AUDIO_BLOCK_SAMPLES);
    Serial.print("agc2: Gain=");
    Serial.print((float)agcGain2/(float)0x10000000);
    Serial.print(" cycles/sample=");
    Serial.println((float)cyc2/(float)AUDIO_BLOCK_SAMPLES);
    Serial.println("");
    last_time = millis();
  }
#endif
}

void transformCoeffs( short int out[], double in[], int k )
{
  for ( int i = 0 ; i < k ; i++ )
    out[i] = 32767 * in[i];
}

void agcCalc(int16_t *inbuf,int16_t *outbuf,int32_t *agcGain)
{
  int16_t *bp1=inbuf,*bp2=outbuf;
#ifdef PASSTHRU
    // Copy from input to output buffer
    for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) 
    {
      *bp2++ = *bp1++;
    }
#else
    // Apply agcGain on input with saturation to output buffer
    int32_t sum=0,sum1=0,sum2=0,sum3=0,res,res1,res2,res3,err;
    for(int i = 0;i < AUDIO_BLOCK_SAMPLES>>2;i++) 
    { 
      //ref. dspinst.h in Teensy audio library
      res=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res1=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res2=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res3=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      *bp2++=saturate16(res);
      *bp2++=saturate16(res1);
      *bp2++=saturate16(res2);
      *bp2++=saturate16(res3);
      sum+=abs(res);
      sum1+=abs(res1);
      sum2+=abs(res2);
      sum3+=abs(res3);
    }
    sum+=(sum1+sum2+sum3);
    // Calculate new agcGain for next input buffer
    err=ref-sum; 
    *agcGain+=err>>3; //update gain 
    *agcGain=min(*agcGain,0x7ff80000); //keep agcGain < 8.0
    *agcGain=max(*agcGain,0); //keep agcGain >= 0
#endif
}


agcBox1.jpgagcBox2.jpggroundLoopEliminator.jpg
 
In fact, I solved my problem. I browsed around the forum and found out that I shouldn't connect audio shield headphones output to line in of another external device (in my case a bluetooth transmitter). I had connected headphones L & R outputs to device L & R inputs and VG(virtual ground) to device Gnd input. I rewired the audio shield line out instead and increased the line out amplitude to compensate (sgtl5000_1.lineOutLevel(13)). No more noise. Can someone please explain to me what VG is and what it's used for? Thanks.
 
Its a Virtual (ac) Ground for the headphone amplifier - its got a DC offset from actual ground, so that the
headphone amp doesn't need large bulky electrolytic coupling caps (this is a chip used in phones etc).

Since the supply is single ended there are only two ways to be able swing an output as AC - a series coupling
capacitor (aka DC-blocker), or using a mid-rail virtual ground. For low impedance headphones (15 ohms) the
coupling caps would need to be about 1000µF or more for full bass response.

The line-outs are high impedance (10k--47k is typical) and a few µF is sufficient, taking up much less space.
 
I guess the trace on the audio shield is just a test point then?
I can see the danger of connecting a virtual ground with a DC offset to an input device(BT xmitter) at REAL ground.
I guess the ground loop noise is introduced by the BT xmitter? When I listened to the sound via headphones, with the BT xmitter disconnected, there is no audible noise.
I guess it shoulda been obvious to not connect the headphone traces to the input of the BT xmitter but I didn't realize that the level at the audio shield lineout could be programmed with sgtl5000_1.lineOutLevel().
I'm doing a lotta guessing and surfing on the forum. I don't want to be critical about such a marvelous product but there is still not enough documentation and application notes for new guy's.

Thanx for your help, you & others on this forum!
 
I guess the trace on the audio shield is just a test point then?
I'd assume its so you can run a remote jack in a chassis for headphones. For a mysterious reason its
not a through-hole though?

If you've specific ideas to improve the webpage about the Audio shield(s) perhaps add here:
https://forum.pjrc.com/threads/60862-Web-site-could-use-a-few-updates-)
I think what's lacking is a couple of example connection setups and an explicit warning about the headphone ground
 
If all you want is AGC, then the audio processing block that you're looking for is a "compressor". I've always been suprised that there isn't one in the Teensy Audio Library.
Chip

View attachment 26052

I implemented my AGC algo. It works great for music. However, I might use your compressor for watching TV movies as my hearing is not so good for comprehension. I found that I can store the test signals directly in Teensy 4.0 program memory. No need for the audio shield using the USB output. I used 0dB & 20dB gains for the sweep test. I modified your program to be able to use the program memory. See attachments, figures. Thanx.

Code:
 /*
   BasicCompressor_Float

   Created: Chip Audette, Dec 2016 - Jan 2017
   Purpose: Process audio by applying a single-band compressor
            Demonstrates audio processing using floating point data type.

   Uses Teensy Audio Adapter.
   Assumes microphones (or whatever) are attached to the LINE IN (stereo)

   MIT License.  use at your own risk.
*/

//Chip Audette blog is:    https://openaudio.blogspot.com/search/label/Compression

//These are the includes from the Teensy Audio Library
#include <Audio.h>   //Teensy Audio Librarya
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

#include <OpenAudio_ArduinoLibrary.h> //for AudioConvert_I16toF32, AudioConvert_F32toI16, and AudioEffectGain_F32

//create audio library objects for handling the audio
#define DO_USB  1    //set to 1 to enable USB audio.  Be sure to go under the "Tools" menu and do "USB Type" -> "Audio"
#if DO_USB
//#define USE_SD //input test wav files can be stored on SD card instead of USB audio
#define USE_PLAYMEM //input test wav files can be stored stored in PROGMEM
#endif 
#ifdef USE_SD
AudioPlaySdWav            playSdWav1;
#endif
#ifdef USE_PLAYMEM
#include "AudioSampleSweepamp.h"
#include "AudioSampleStepTone.h"
AudioPlayMemory    sound0;
#endif
AudioControlSGTL5000_Extended    sgtl5000;    //controller for the Teensy Audio Board
AudioInputI2S             i2s_in;             //Digital audio *from* the Teensy Audio Board ADC.  Sends Int16.  Stereo.
AudioConvert_I16toF32     int2Float1, int2Float2;    //Converts Int16 to Float.  See class in AudioStream_F32.h
AudioEffectCompressor_F32 comp1, comp2;
AudioConvert_F32toI16     float2Int1, float2Int2;    //Converts Float to Int16.  See class in AudioStream_F32.h
AudioOutputI2S            i2s_out;            //Digital audio *to* the Teensy Audio Board DAC.  Expects Int16.  Stereo

//Make all of the audio connections, with the option of USB audio in and out
//note that you ALWAYS have to have an I2S connection (either in or out) to have a clock
//that drives the system.

#if DO_USB
#if defined(USE_SD) || defined(USE_PLAYMEM)
#ifdef USE_SD
      AudioConnection           patchCord1(playSdWav1, 0, int2Float1, 0);
      AudioConnection           patchCord2(playSdWav1, 1, int2Float2, 0);
#endif
#ifdef USE_PLAYMEM
      AudioConnection           patchCord1(sound0, 0, int2Float1, 0);
      AudioConnection           patchCord2(sound0, 0, int2Float2, 0);
#endif
#else
    AudioInputUSB           usb_in;
    AudioConnection         patchCord1(usb_in, 0, int2Float1, 0);
    AudioConnection         patchCord2(usb_in, 1, int2Float2, 0);
#endif
#else
  AudioConnection         patchCord1(i2s_in, 0, int2Float1, 0);   //connect the Left input to the Left Int->Float converter
  AudioConnection         patchCord2(i2s_in, 1, int2Float2, 0);   //connect the Right input to the Right Int->Float converter
#endif
AudioConnection_F32     patchCord10(int2Float1, 0, comp1, 0); //Left.  makes Float connections between objects
AudioConnection_F32     patchCord11(int2Float2, 0, comp2, 0); //Right.  makes Float connections between objects
AudioConnection_F32     patchCord12(comp1, 0, float2Int1, 0); //Left.  makes Float connections between objects
AudioConnection_F32     patchCord13(comp2, 0, float2Int2, 0); //Right.  makes Float connections between objects
AudioConnection         patchCord20(float2Int1, 0, i2s_out, 0);  //connect the Left float processor to the Left output
AudioConnection         patchCord21(float2Int2, 0, i2s_out, 1);  //connect the Right float processor to the Right output
#if DO_USB
  AudioOutputUSB          usb_out;
  AudioConnection         patchCord30(float2Int1, 0, usb_out, 0);  //connect the Left float processor to the Left output
  AudioConnection         patchCord31(float2Int2, 0, usb_out, 1);  //connect the Right float processor to the Right output
#endif
#if defined(USE_SD) || defined(USE_PLAYMEM)
#ifdef USE_SD
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14
#endif
#else 
  // which input on the audio shield will be used?
  const int myInput = AUDIO_INPUT_LINEIN;
  //const int myInput = AUDIO_INPUT_MIC;
#endif
//I have a potentiometer on the Teensy Audio Board
#define POT_PIN A0  //potentiometer is tied to this pin

//define a function to setup the Teensy Audio Board how I like it
void setupMyAudioBoard(void) {
  sgtl5000.enable();                   //start the audio board
  sgtl5000.volume(0.8);                //volume can be 0.0 to 1.0.  0.5 seems to be the usual default.
#if defined(USE_SD) || defined(USE_PLAYMEM)
#ifdef USE_SD
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
  delay(1000);
#endif
#else
  sgtl5000.inputSelect(myInput);       //choose line-in or mic-in
#endif
  sgtl5000.lineInLevel(10, 10);        //level can be 0 to 15.  5 is the Teensy Audio Library's default
  sgtl5000.adcHighPassFilterDisable(); //reduces noise.  https://forum.pjrc.com/threads/27215-24-bit-audio-boards?p=78831&viewfull=1#post78831
  sgtl5000.micBiasEnable(3.0);         //enable the mic bias voltage...only in AudioControlSGTL5000_Extended
}

//define a function to configure the left and right compressors
void setupMyCompressors(boolean use_HP_filter, float knee_dBFS, float comp_ratio, float attack_sec, float release_sec) {
  comp1.enableHPFilter(use_HP_filter);   comp2.enableHPFilter(use_HP_filter);
  comp1.setThresh_dBFS(knee_dBFS);       comp2.setThresh_dBFS(knee_dBFS);
  comp1.setCompressionRatio(comp_ratio); comp2.setCompressionRatio(comp_ratio);

  float fs_Hz = AUDIO_SAMPLE_RATE;
  comp1.setAttack_sec(attack_sec, fs_Hz);       comp2.setAttack_sec(attack_sec, fs_Hz);
  comp1.setRelease_sec(release_sec, fs_Hz);     comp2.setRelease_sec(release_sec, fs_Hz);
}

// define the overall setup() function, the function that is called once when the device is booting
void setup() {
  Serial.begin(115200);   //open the USB serial link to enable debugging messages
  delay(500);             //give the computer's USB serial system a moment to catch up.
  Serial.println("Teensy Hearing Aid: BasicCompressor_Float..."); //identify myself over the USB serial

  // Audio connections require memory, and the record queue uses this memory to buffer incoming audio.
  AudioMemory(14);  //allocate Int16 audio data blocks
  AudioMemory_F32(16); //allocate Float32 audio data blocks

  // Enable the audio shield, select input, and enable output
  setupMyAudioBoard();

  //choose the compressor parameters...note that preGain is set by the potentiometer in the main loop()
  boolean use_HP_filter = true; //enable the software HP filter to get rid of DC?
  float knee_dBFS, comp_ratio, attack_sec, release_sec;
  if (true) {
      Serial.println("Configuring Compressor for fast response for use as a limitter.");
      knee_dBFS = -15.0f; comp_ratio = 5.0f;  attack_sec = 0.005f;  release_sec = 0.200f;
  } else {
      Serial.println("Configuring Compressor for slow response more like an automatic volume control.");
      knee_dBFS = -50.0; comp_ratio = 5.0;  attack_sec = 1.0;  release_sec = 2.0;
  }

  //configure the left and right compressors with the desired settings
  setupMyCompressors(use_HP_filter, knee_dBFS, comp_ratio, attack_sec, release_sec);

  // setup any other other features
  pinMode(POT_PIN, INPUT); //set the potentiometer's input pin as an INPUT

} //end setup()


// define the loop() function, the function that is repeated over and over for the life of the device
unsigned long updatePeriod_millis = 100; //how many milliseconds between updating gain reading?
unsigned long lastUpdate_millis = 0;
unsigned long curTime_millis = 0;
int prev_gain_dB = 0;
unsigned long lastMemUpdate_millis = 0;
void loop() {
#if defined(USE_SD) || defined(USE_PLAYMEM)
#ifdef USE_SD
  if (playSdWav1.isPlaying() == false) 
  {
    Serial.println("Start playing");
    playSdWav1.play("sweepamp.wav");
//    playSdWav1.play("steptone.wav");
    delay(10);
  }
#endif
#ifdef USE_PLAYMEM
  if (sound0.isPlaying() == false)
  {
    //sound0.play(AudioSampleSweepamp);
    sound0.play(AudioSampleSteptone);
  }
#endif
#else
  //choose to sleep ("wait for interrupt") instead of spinning our wheels doing nothing but consuming power
  asm(" WFI");  //ARM-specific.  Will wake on next interrupt.  The audio library issues tons of interrupts, so we wake up often.
#endif
  //has enough time passed to try updating the GUI?
  curTime_millis = millis(); //what time is it right now
  if (curTime_millis < lastUpdate_millis) lastUpdate_millis = 0; //handle wrap-around of the clock
  if ((curTime_millis - lastUpdate_millis) > updatePeriod_millis) { //is it time to update the user interface?

    //read potentiometer
    float32_t val = float(analogRead(POT_PIN)) / 1024.0f; //0.0 to 1.0
    val = 0.1 * (float)((int)(10.0 * val + 0.5)); //quantize so that it doesn't chatter

    //compute desired digital gain
    const float min_gain_dB = -20.0, max_gain_dB = 40.0; //set desired gain range
    float gain_dB = min_gain_dB + (max_gain_dB - min_gain_dB) * val; //computed desired gain value in dB

    //if the gain is different than before, set the new gain value
    if (abs(gain_dB - prev_gain_dB) > 1.0) { //is it different than before
      prev_gain_dB = gain_dB;  //we will use this value the next time around
      
      gain_dB = 0.0; //force to 0 dB for debugging
      //gain_dB = 20.0;
      
      comp1.setPreGain_dB(gain_dB);  //set the gain of the Left-channel gain processor
      comp2.setPreGain_dB(gain_dB);  //set the gain of the Right-channel gain processor
      Serial.print("Setting Digital Pre-Gain dB = "); Serial.println(gain_dB); //print text to Serial port for debugging
    }

    lastUpdate_millis = curTime_millis; //we will use this value the next time around.
  } // end if


  //print status information to the Serial port
  if ((curTime_millis - lastMemUpdate_millis) > 2000) {  // print a summary of the current & maximum usage
    //printCompressorState(&Serial);
    printCPUandMemoryUsage(&Serial);
    lastMemUpdate_millis = curTime_millis; //we will use this value the next time around.
  }

} //end loop();

void printCompressorState(Stream *s) {
  s->print("Current Compressor: Pre-Gain (dB) = ");
  s->print(comp1.getPreGain_dB());
  s->print(", Level (dBFS) = ");
  s->print(comp1.getCurrentLevel_dBFS());
  s->print(", ");
  s->print(comp2.getCurrentLevel_dBFS());
  s->print(", Dynamic Gain L/R (dB) = ");
  s->print(comp1.getCurrentGain_dB());
  s->print(", ");
  s->print(comp2.getCurrentGain_dB());
  s->println();
};

void printCPUandMemoryUsage(Stream *s) {
  s->print("Usage/Max: ");
  s->print("comp1 CPU = "); s->print(comp1.processorUsage()); s->print("/"); s->print(comp1.processorUsageMax()); s->print(", ");
  s->print("all CPU = " ); s->print(AudioProcessorUsage()); s->print("/");  s->print(AudioProcessorUsageMax()); s->print(", ");
  s->print("Int16 Mem = "); s->print(AudioMemoryUsage()); s->print("/"); s->print(AudioMemoryUsageMax()); s->print(", ");
  s->print("Float Mem = "); s->print(AudioMemoryUsage_F32()); s->print("/"); s->print(AudioMemoryUsageMax_F32()); s->print(", ");
  s->println();
};

sweep.jpg step.jpg
 
If all you want is AGC, then the audio processing block that you're looking for is a "compressor". I've always been suprised that there isn't one in the Teensy Audio Library. I'd do one myself, but my integer math skills are lacking. Instead...

You're running a Teensy 4, which means that you can do floating point math as fast as integer math. Therefore, you are not limited to the Teensy Audio Library (with it's focus on integer math). There are several folks with floating-point extensions of the Teensy Audio Library. You can run any one of them without penalty.

One choice is the OpenAudio library (https://github.com/chipaudette/OpenAudio_ArduinoLibrary). I started it, but it is now maintained by others. Most important for you is that it has two different compressors that you can use. I'd start with the simpler one. One of the examples that comes with the library shows you how to use it: https://github.com/chipaudette/OpenAudio_ArduinoLibrary/tree/master/examples/BasicCompressor_Float

Here's a blog post explaining its approach...it's a totally standard dynamic range compressor: https://openaudio.blogspot.com/search/label/Compression

Chip

View attachment 26052

Hello,

I have now successfully integrated the Compressor code AND the AGC code on a single platform. There is a Teensy 4.0, an Audio Adapter, an OLED display controlled by an ATMega328(3.3V 8 MHz), and a pushbutton for selecting AGCmode(music) or CompressorMode(voice for TV, films for the old slightly deaf folks like me). I find that the compressor with 20dB of gain works VERY WELL for understanding dialogs in a film or fast-talking weather announcers who are pressed to spit out their forecast before the next commercial. I heard somewhere that you or someone was thinking about adding some filtering to the compressor to accent certain frequencies(especially highfreq's). I was wondering if there is any news on this subject. Included are the details(if you're interested) of my implementation. Thanx


1_AudioSetup.jpg 2_SetupCompressorAGC.jpg 3_agcBox3.jpg 4_agcBox2.jpg 5_agcMode.jpg 6_compressorMode.jpg
Code:
/*
   BasicCompressor_Float

   Created: Chip Audette, Dec 2016 - Jan 2017
   Purpose: Process audio by applying a single-band compressor
            Demonstrates audio processing using floating point data type.

   Uses Teensy Audio Adapter.
   Assumes microphones (or whatever) are attached to the LINE IN (stereo)

   MIT License.  use at your own risk.
*/

//Chip Audette blog is:    https://openaudio.blogspot.com/search/label/Compression
//This code has been modified to have 2 functioning MODES 1. MODE_Compressor(for TV movies or programs) or 2. MODE_AGC(for music) 

//These are the includes from the Teensy Audio Library
#include <Audio.h>   //Teensy Audio Librarya
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>
#include <OpenAudio_ArduinoLibrary.h> //for AudioConvert_I16toF32, AudioConvert_F32toI16, and AudioEffectGain_F32

#define CompressorInput 0 //used for mixers
#define AGCInput 1

//create audio library objects for handling the audio
 
//------------- compressor objects ---------------------
AudioControlSGTL5000_Extended    sgtl5000;    //controller for the Teensy Audio Board
AudioInputI2S             i2s_in;             //Digital audio *from* the Teensy Audio Board ADC.  Sends Int16.  Stereo.
AudioConvert_I16toF32     int2Float1, int2Float2;    //Converts Int16 to Float.  See class in AudioStream_F32.h
AudioEffectCompressor_F32 comp1, comp2;
AudioConvert_F32toI16     float2Int1, float2Int2;    //Converts Float to Int16.  See class in AudioStream_F32.h
AudioOutputI2S            i2s_out;            //Digital audio *to* the Teensy Audio Board DAC.  Expects Int16.  Stereo

//-------------- agc objects ------------
AudioRecordQueue         queue_inLeft; 
AudioRecordQueue         queue_inRight;
AudioPlayQueue           queue_outLeft; 
AudioPlayQueue           queue_outRight;
AudioMixer4              mixerLeft; 
AudioMixer4              mixerRight; 

//---------------- compressor connections --------------------------------------------------
AudioConnection         patchCord1(i2s_in, 0, int2Float1, 0);   //connect the Left input to the Left Int->Float converter
AudioConnection         patchCord2(i2s_in, 1, int2Float2, 0);   //connect the Right input to the Right Int->Float converter
AudioConnection_F32     patchCord10(int2Float1, 0, comp1, 0); //Left.  makes Float connections between objects
AudioConnection_F32     patchCord11(int2Float2, 0, comp2, 0); //Right.  makes Float connections between objects
AudioConnection_F32     patchCord12(comp1, 0, float2Int1, 0); //Left.  makes Float connections between objects
AudioConnection_F32     patchCord13(comp2, 0, float2Int2, 0); //Right.  makes Float connections between objects
AudioConnection         patchCord20(float2Int1, 0, mixerLeft, CompressorInput);
AudioConnection         patchCord21(float2Int2, 0, mixerRight, CompressorInput);
AudioConnection         patchCord30(mixerLeft, 0, i2s_out, 0);
AudioConnection         patchCord31(mixerRight, 0, i2s_out, 1);

//-------------- agc connections ------------------
AudioConnection         patchCord1a(i2s_in, 0, queue_inLeft, 0);
AudioConnection         patchCord2a(i2s_in, 1, queue_inRight, 0);
AudioConnection         patchCord10a(queue_outLeft, 0, mixerLeft, AGCInput);
AudioConnection         patchCord11a(queue_outRight, 0, mixerRight, AGCInput);

Bounce button = Bounce(14, 15); //pin14 15msec debounce time

// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;

//Global AGC Variables 
int32_t agcGain1=0x08000000;//.5 in Q3.29 format
int32_t agcGain2=0x08000000;//.5 in Q3.29 format
int32_t ref=146752;//.035*AUDIO_BLOCK_SAMPLES in Q15 format(1146*128)
int SelectCompressor=0; //starting mode is AGC

//define a function to setup the Teensy Audio Board how I like it
void setupMyAudioBoard(void) {
  sgtl5000.enable();                   //start the audio board
  sgtl5000.volume(0.8);                //volume can be 0.0 to 1.0.  0.5 seems to be the usual default.
  sgtl5000.inputSelect(myInput);       //choose line-in or mic-in
//  sgtl5000.lineInLevel(10, 10);        //level can be 0 to 15.  5 is the Teensy Audio Library's default
  sgtl5000.adcHighPassFilterDisable(); //reduces noise.  https://forum.pjrc.com/threads/27215-24-bit-audio-boards?p=78831&viewfull=1#post78831
  sgtl5000.micBiasEnable(3.0);         //enable the mic bias voltage...only in AudioControlSGTL5000_Extended
}

//define a function to configure the left and right compressors
void setupMyCompressors(boolean use_HP_filter, float knee_dBFS, float comp_ratio, float attack_sec, float release_sec) {
  comp1.enableHPFilter(use_HP_filter);   comp2.enableHPFilter(use_HP_filter);
  comp1.setThresh_dBFS(knee_dBFS);       comp2.setThresh_dBFS(knee_dBFS);
  comp1.setCompressionRatio(comp_ratio); comp2.setCompressionRatio(comp_ratio);

  float fs_Hz = AUDIO_SAMPLE_RATE;
  comp1.setAttack_sec(attack_sec, fs_Hz);       comp2.setAttack_sec(attack_sec, fs_Hz);
  comp1.setRelease_sec(release_sec, fs_Hz);     comp2.setRelease_sec(release_sec, fs_Hz);
}

//Select processed Compressor or AGC audio for the output
void updateMode(int SelCompressor) //select Compressor or AGC outputs
{
  if (SelCompressor)
  {
    mixerLeft.gain(CompressorInput,1.0); //connect Compressor to output
    mixerRight.gain(CompressorInput,1.0);
    mixerLeft.gain(AGCInput,0.0);
    mixerRight.gain(AGCInput,0.0);
  }
  else
  {
    mixerLeft.gain(CompressorInput,0.0); //connect AGC to output
    mixerRight.gain(CompressorInput,0.0);
    mixerLeft.gain(AGCInput,1.0);
    mixerRight.gain(AGCInput,1.0);   
  }
}

// define the overall setup() function, the function that is called once when the device is booting
void setup() {
  Serial.begin(115200);   //open the USB serial link to enable debugging messages
  Serial4.begin(9600); //serial interface to UNO OLED controller
  delay(500);             //give the computer's USB serial system a moment to catch up.
  Serial.println("Teensy Hearing Aid: BasicCompressor_Float..."); //identify myself over the USB serial

  // Audio connections require memory, and the record queue uses this memory to buffer incoming audio.
  AudioMemory(32);  //allocate Int16 audio data blocks
  AudioMemory_F32(16); //allocate Float32 audio data blocks
  
  // Enable the audio shield, select input, and enable output
  setupMyAudioBoard();

  //choose the compressor parameters...note that preGain is set by the potentiometer in the main loop()
  boolean use_HP_filter = true; //enable the software HP filter to get rid of DC?
  float knee_dBFS, comp_ratio, attack_sec, release_sec;
  if (true) {
      Serial.println("Configuring Compressor for fast response for use as a limitter.");
      knee_dBFS = -15.0f; comp_ratio = 5.0f;  attack_sec = 0.005f;  release_sec = 0.200f;
  } else {
      Serial.println("Configuring Compressor for slow response more like an automatic volume control.");
      knee_dBFS = -50.0; comp_ratio = 5.0;  attack_sec = 1.0;  release_sec = 2.0;
  }

  //configure the left and right compressors with the desired settings
  setupMyCompressors(use_HP_filter, knee_dBFS, comp_ratio, attack_sec, release_sec);

  //configure compressor pregains
  float gain_dB = 20.0;
  comp1.setPreGain_dB(gain_dB);  //set the gain of the Left-channel gain processor
  comp2.setPreGain_dB(gain_dB);  //set the gain of the Right-channel gain processor
  
  pinMode(14, INPUT_PULLUP); //pushbutton to change AGC/Compressor modes
  updateMode(SelectCompressor); //Initially configure to AGC mode (SelectCompressor=0)

  delay(100);
  
  // Start the record queue
  queue_inLeft.begin();
  queue_inRight.begin();
  
} //end setup()

// define the loop() function, the function that is repeated over and over for the life of the device
unsigned long curTime_millis = 0;
unsigned long lastMemUpdate_millis = 0;
unsigned long last_time1 = millis();
void loop() {
  
//Left channel AGC 
  if (queue_inLeft.available() >= 1) //Left Channel
  {
    agcCalc(queue_inLeft.readBuffer(),queue_outLeft.getBuffer(),&agcGain1);
    // Free the input audio buffer
    queue_inLeft.freeBuffer();
    // and play it back into the audio queue
    queue_outLeft.playBuffer();
  } //if (queue_inLeft.available() >= 1)
  
//Right channel AGC 
  if (queue_inRight.available() >= 1) 
  {
    agcCalc(queue_inRight.readBuffer(),queue_outRight.getBuffer(),&agcGain2);
    // Free the input audio buffer
    queue_inRight.freeBuffer();
    // and play it back into the audio queue
    queue_outRight.playBuffer();
  } //if (queue_inRight.available() >= 1)

  //print status information to the Serial port
  if ((curTime_millis - lastMemUpdate_millis) > 2000) {  // print a summary of the current & maximum usage
    //printCompressorState(&Serial);
    printCPUandMemoryUsage(&Serial);
    lastMemUpdate_millis = curTime_millis; //we will use this value the next time around.
  }
  
  if (millis() - last_time1 >= 2500) //Send info to OLED display via Arduino UNO
  {
    if (SelectCompressor)
    {
      Serial4.print("CompressorActive");
    }
    else
    {
      Serial4.print("agcGains: ");
      Serial4.print((float)agcGain1/(float)0x10000000);
      Serial4.print(",");
      Serial4.println((float)agcGain2/(float)0x10000000);
    }
    last_time1=millis();
  }
  
  button.update(); //push button to change mode AGC/Compressor
  if (button.fallingEdge()) 
  {
    if (SelectCompressor==0)
      SelectCompressor=1;
    else
      SelectCompressor=0;
    updateMode(SelectCompressor);
  }
} //end loop();

void printCompressorState(Stream *s) {
  s->print("Current Compressor: Pre-Gain (dB) = ");
  s->print(comp1.getPreGain_dB());
  s->print(", Level (dBFS) = ");
  s->print(comp1.getCurrentLevel_dBFS());
  s->print(", ");
  s->print(comp2.getCurrentLevel_dBFS());
  s->print(", Dynamic Gain L/R (dB) = ");
  s->print(comp1.getCurrentGain_dB());
  s->print(", ");
  s->print(comp2.getCurrentGain_dB());
  s->println();
};

void printCPUandMemoryUsage(Stream *s) {
  s->print("Usage/Max: ");
  s->print("comp1 CPU = "); s->print(comp1.processorUsage()); s->print("/"); s->print(comp1.processorUsageMax()); s->print(", ");
  s->print("all CPU = " ); s->print(AudioProcessorUsage()); s->print("/");  s->print(AudioProcessorUsageMax()); s->print(", ");
  s->print("Int16 Mem = "); s->print(AudioMemoryUsage()); s->print("/"); s->print(AudioMemoryUsageMax()); s->print(", ");
  s->print("Float Mem = "); s->print(AudioMemoryUsage_F32()); s->print("/"); s->print(AudioMemoryUsageMax_F32()); s->print(", ");
  s->println();
};

void agcCalc(int16_t *inbuf,int16_t *outbuf,int32_t *agcGain)
{
    int16_t *bp1=inbuf,*bp2=outbuf;
    // Apply agcGain on input with saturation to output buffer
    int32_t sum=0,sum1=0,sum2=0,sum3=0,res,res1,res2,res3,err;
    for(int i = 0;i < AUDIO_BLOCK_SAMPLES>>2;i++) 
    { 
      //ref. dspinst.h in Teensy audio library
      res=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res1=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res2=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      res3=signed_multiply_32x16b(*agcGain>>12,*bp1++);
      *bp2++=saturate16(res);
      *bp2++=saturate16(res1);
      *bp2++=saturate16(res2);
      *bp2++=saturate16(res3);
      sum+=abs(res);
      sum1+=abs(res1);
      sum2+=abs(res2);
      sum3+=abs(res3);
    }
    sum+=(sum1+sum2+sum3);
    // Calculate new agcGain for next input buffer
    err=ref-sum; 
    *agcGain+=err>>3; //update gain 
    *agcGain=min(*agcGain,0x40000000); //keep agcGain < 4.0
    *agcGain=max(*agcGain,0); //keep agcGain >= 0
}
Code:
 #include <Wire.h>
#include <SPI.h>
#include <SoftwareSerial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
//SDA=A4
//SCL=A5
Adafruit_SSD1306 display(OLED_RESET);
SoftwareSerial Seriala(6, 7);
unsigned long last_time = millis();
char readString[]="agcGains: 0.5,0.5";
void setup() {
  Serial.begin(115200);
  Seriala.begin(9600);
  Wire.begin();
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setRotation(2); //add this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  displayOLED();
  display.display();  
  delay(2000);
  while (Seriala.available()>0) Seriala.read(); //start with an MT softwareserial buffer
}


void loop()
{
  char c;
  int i;
  unsigned long WaitTime,StartTime;
  if (Seriala.available()>0)
  {
    i=0;
    WaitTime=1000UL;
    StartTime=millis();
    while((millis()-StartTime)<=WaitTime);
    {
    }
    while (Seriala.available()>0)
    {
      c=Seriala.read();
      readString[i]=c;
      i++;
    }
    readString[i]='\0'; //terminate string
  }
  if (millis() - last_time >= 2500)
  {
    displayOLED();
    display.display();
    last_time = millis();  
  }
}


void displayOLED(){
  display.clearDisplay();
  display.setCursor(0,0);
  display.print(readString);
}
 
Status
Not open for further replies.
Back
Top