Feasibility: Audio Synth + FFT + USB Host?

Status
Not open for further replies.

rozling

Member
Hi,

I've been having a great time with my Teensy 3.6 / Audio Shield and sending FFT data to an OLED display.

IMG_1952 (1).jpg

As the whole thing is currently on a breadboard, one thing I wanted to do was make the Teensy into a USB host.

This way I can use my durable €20 HID MIDI DJ controller and hand that to my two-year-old when she wants to play with the synth instead of her pulling out components all over the place :)

I already had the Analog inputs -> Synth -> FFT -> Display part working, and last night I managed to get the USB host working too.

However the update rate is painfully slow, even keeping calculations to a minumum. When I turn a MIDI knob it can take a few seconds for the synth parameter to catch up. It seems to slowly go through every value from the original position down to the target position, even if I move the knob quickly.

When I run the USB Host demo it outputs values to the serial port in realtime, with no delay. Similarly the Audio Shield sketch is really responsive when twiddling knobs.

It's only when I combine the two that there's an issue, which makes me wonder: am I simply asking too much of the Teensy 3.6?

I do have a 4.0 on the way so can test it when it arrives, but that might take a while...

Here's my code - would appreciate it if anyone notices something glaringly wrong!

Code:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Audio.h>
#include <Bounce.h>
// #include <SD.h>
#include <SerialFlash.h>
#include <SPI.h>
#include "USBHost_t36.h"
#include <Wire.h>

Bounce button0 = Bounce(0, 15);
Bounce button1 = Bounce(1, 15);  // 15 = 15 ms debounce time
Bounce button2 = Bounce(2, 15);

#define OLED_RESET 5 
Adafruit_SSD1306 display(OLED_RESET);

// stuff for Adafruit logo
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=740,490
AudioSynthWaveformSine   sine1;          //xy=761,618
AudioSynthNoisePink      pink1;          //xy=764,692
AudioSynthWaveformSineModulated sine_fm1;       //xy=805,562
AudioMixer4              mixer1;         //xy=974,580
AudioEffectEnvelope      envelope1;      //xy=1003.7500190734863,731.7500114440918
AudioMixer4              mixer2;         //xy=1151.500015258789,653.2500076293945
AudioAnalyzeFFT1024      LeftFFT;      //xy=1355.0000190734863,682.5000114440918
AudioAnalyzeFFT1024      RightFFT;      //xy=1358,744
AudioOutputI2S           i2s1;           //xy=1393,540
AudioConnection          patchCord1(waveform1, 0, mixer1, 0);
AudioConnection          patchCord2(waveform1, sine_fm1);
AudioConnection          patchCord3(sine1, 0, mixer1, 2);
AudioConnection          patchCord4(pink1, 0, mixer1, 3);
AudioConnection          patchCord5(sine_fm1, 0, mixer1, 1);
AudioConnection          patchCord6(mixer1, 0, mixer2, 0);
AudioConnection          patchCord7(mixer1, envelope1);
AudioConnection          patchCord8(envelope1, 0, mixer2, 1);
AudioConnection          patchCord9(mixer2, 0, i2s1, 0);
AudioConnection          patchCord10(mixer2, 0, i2s1, 1);
AudioConnection          patchCord11(mixer2, LeftFFT);
AudioConnection          patchCord12(mixer2, RightFFT);
AudioControlSGTL5000     sgtl5000_1;     //xy=953,944
// GUItool: end automatically generated code

// USB Setup
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
MIDIDevice midi1(myusb);

float knob1 = 0;
float knob2 = 0;
float knob3 = 0;

void setup() {
  Serial.begin(9600);
  AudioMemory(20);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.32);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  display.display();
  delay(2000);

  display.clearDisplay();

  display.setCursor(0, 0);
  
  // SET UP PINS
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);

  // SET UP MIXER
  mixer1.gain(0, 0.75);
  mixer1.gain(1, 0.0);
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);
  mixer2.gain(0, 0.15);
  mixer2.gain(1, 0.0);
  mixer2.gain(2, 0.0);
  mixer2.gain(3, 0.0);

  // SET UP SYNTH
  waveform1.begin(WAVEFORM_SAWTOOTH);
  waveform1.amplitude(0.75);
  waveform1.frequency(50);
  waveform1.pulseWidth(0.15);
  sine_fm1.frequency(440);
  sine_fm1.amplitude(0.75);
  sine1.frequency(200);
  sine1.amplitude(0.75);
  pink1.amplitude(0.75);
  envelope1.attack(10);
  envelope1.hold(10);
  envelope1.decay(25);
  envelope1.sustain(0.4);
  envelope1.release(70);
  
  // SET UP USB
  myusb.begin();
  midi1.setHandleNoteOff(OnNoteOff);
  midi1.setHandleNoteOn(OnNoteOn);
  midi1.setHandleControlChange(OnControlChange);
}

int waveform_type = WAVEFORM_SAWTOOTH;
int mixer1_setting = 0;
int mixer2_setting = 0;
elapsedMillis timeout = 0;
bool mixer2_envelope = false;

unsigned long last_time = millis();
uint8_t overlayCounter = 0;
float lastLoopTime = 0;
uint16_t lastCPU = 0;
uint16_t lastMem = 0;

// Initialise FFT array
float leftBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

float RightBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};


void loop() {
  button0.update();
  button1.update();
  button2.update();

  myusb.Task();
  midi1.read();

  display.setCursor(0, 0);

  // Left changes the type of control waveform
  if (button0.fallingEdge()) {
    Serial.print("Control waveform: ");
    if (waveform_type == WAVEFORM_SAWTOOTH) {
      waveform_type = WAVEFORM_SINE;
      Serial.println("Sine");
    } else if (waveform_type == WAVEFORM_SINE) {
      waveform_type = WAVEFORM_SQUARE;
      Serial.println("Square");
    } else if (waveform_type == WAVEFORM_SQUARE) {
      waveform_type = WAVEFORM_TRIANGLE;
      Serial.println("Triangle");
    } else if (waveform_type == WAVEFORM_TRIANGLE) {
      waveform_type = WAVEFORM_PULSE;
      Serial.println("Pulse");
    } else if (waveform_type == WAVEFORM_PULSE) {
      waveform_type = WAVEFORM_SAWTOOTH;
      Serial.println("Sawtooth");
    }
    waveform1.begin(waveform_type);
  }

  // Check middle button
  // If pressed, Cycle sequentially through various mixer settings
  if (button1.fallingEdge()) {
    if (mixer1_setting == 0) {
      mixer1.gain(0, 0.75);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Control oscillator");
      mixer1_setting = 1;
    } else if (mixer1_setting == 1) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.75);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Frequency Modulated Oscillator");
      mixer1_setting = 2;
    } else if (mixer1_setting == 2) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.75);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Regular Sine Wave Oscillator");
      mixer1_setting = 3;
    } else if (mixer1_setting == 3) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.75);
      Serial.println("Mixer1: Pink Noise");
      mixer1_setting = 0;
    }
  }

  // Check right button
  // If pressed, activate the envelope
  if (button2.fallingEdge()) {
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 1.0);
    mixer2_envelope = true;
    timeout = 0;
    envelope1.noteOn();
  }
  if (button2.risingEdge()) {
    envelope1.noteOff();
    timeout = 0;
  }

  // after 4 seconds of inactivity, go back to
  // steady listening intead of the envelope
  if (mixer2_envelope == true && timeout > 4000) {
    mixer2.gain(0, 0.15);
    mixer2.gain(1, 0.0);
    mixer2_envelope = false;
  }

  // use the knobs to adjust parameters
  // float knob1 = (float)analogRead(A1) / 1023.0;
  // float knob2 = (float)analogRead(A2) / 1023.0;
  // float knob3 = (float)analogRead(A3) / 1023.0;


  // some messy remnants of approaches that didn't quite work

  //  knob2 = knob2 / 128;
  //  knob3 = knob3 / 128;
  //  Serial.print("\tknob2 = ");
  //  Serial.print(knob2);
  //  Serial.print("\tknob3 = ");
  //  Serial.println(knob3);

  //  knob2 = (float)analogRead(A2) / 1023.0;
   // knob3 = (float)analogRead(A3) / 1023.0;
  //  waveform1.frequency(360 * knob2 + 0.25);
  //  sine_fm1.frequency(knob3 * 1500 + 50);
  //  sine1.frequency(knob3 * 1500 + 50);

  //  float knob2val = 360 * knob2 + 0.25;
  //  float knob3val = knob3 * 1500 + 50;
  //  Serial.print("knob1 = ");
  //  Serial.println(knob1);
  //  Serial.print("\tknob2 = ");
  //  Serial.print(knob2val);
  //  Serial.print("\tknob3 = ");
  //  Serial.println(knob3val);

  knob1 = (float)analogRead(A1) / 1023.0;

  sgtl5000_1.volume(knob1); 
  waveform1.frequency(knob2);
  sine_fm1.frequency(knob3);
  sine1.frequency(knob3);

  // Set up Overlay
  float loopTime;
  int i;

  //calc loopTime
  unsigned long this_time = millis();
  if (this_time > last_time)
  {
    loopTime = (this_time - last_time);
  }
  last_time = this_time;

  //Update data every 20 frames for readability
  overlayCounter++;
  if (overlayCounter > 20)
  {
    lastLoopTime = loopTime;
    lastCPU = AudioProcessorUsageMax();
    AudioProcessorUsageMaxReset();
    lastMem = AudioMemoryUsageMax();
    AudioMemoryUsageMaxReset();

    overlayCounter = 0;
  }

  //Draw a frame
  display.clearDisplay();

  //Draw left FFT bands
  for (i = 0; i < 40; i++)
  {
    if (leftBands[i] > 0.5) leftBands[i] = 0.25;
    display.drawLine(62 - i, 31, 62 - i, 31 - (int) (leftBands[i] * 1023), WHITE);
  }

  //Draw Right FFT bands
  for (i = 0; i < 40; i++)
  {
    if (RightBands[i] > 0.5) RightBands[i] = 0.25;
    display.drawLine(65 + i, 31, 65 + i, 31 - (int) (RightBands[i] * 1023), WHITE);
  }

    // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  //  display.println("Hello, world!");

  //Overlay info
  //  loop time
  display.setCursor(0, 0);
  display.print("Loop=");
  display.print((uint8_t)lastLoopTime);
  display.print("ms");
  //  Teensy Audio info
  display.setCursor(83, 0);
  display.print("cpu=");
  display.print(lastCPU);
  display.setCursor(91, 8);
  display.print("mem=");
  display.print(lastMem);
  //  L/R letters
  display.setCursor(15, 24);
  display.print("L");
  display.setCursor(108, 24);
  display.print("R");

  // TODO: should this be higher?
  if (LeftFFT.available()) {
    // each time new FFT data is available
    for (i = 0; i < 40; i++) {
      leftBands[i] = LeftFFT.read(i);
    }
  }
  if (RightFFT.available()) {
    // each time new FFT data is available
    for (i = 0; i < 40; i++) {
      RightBands[i] = RightFFT.read(i);
    }
  }

  display.display();
}

void OnNoteOn(byte channel, byte note, byte velocity)
{
  Serial.print("Note On, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  Serial.print(", velocity=");
  Serial.print(velocity);
  Serial.println();
}

void OnNoteOff(byte channel, byte note, byte velocity)
{
  Serial.print("Note Off, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  //Serial.print(", velocity=");
  //Serial.print(velocity);
  Serial.println();
}

void OnControlChange(byte channel, byte control, byte value)
{
//  Commented these in case they were causing a delay:

//  Serial.print("Control Change, ch=");
//  Serial.print(channel);
//  Serial.print(", control=");
//  Serial.print(control);
//  Serial.print(", value=");
//  Serial.print(value);
//  Serial.println();
  int mappedValue;
    switch (control) {
//  reverted this to faster analog pot input due to nasty volume
//  surprises :(

//  case 8: //volume
//    mappedValue = constrain(value, 1,100);
//    Serial.print("Value: ");
//    Serial.println(mappedValue);
//    knob1 = mappedValue;
//    break;
  case 23: //knob2
    mappedValue = map(value, 1,128, 1, 356);
    Serial.println(mappedValue);
    knob2 = mappedValue;
    break;
  case 11: //knob3
    mappedValue = map(value, 1,128, 60, 1500);
    Serial.println(mappedValue);
    knob3 = mappedValue;
    break;
  default:
    break;
  }

}
 
I'd say it's because you're refreshing the whole displays in every loop. The display is really slow. So you may want to modify the code. And yes, a 3.6 is fast enough.
 
Thanks for the quick reply :)

The thing is, the same code minus the USB stuff works fine, no delays at all - and the time for the loop to complete stays the same, somewhere around 56ms/57ms:

Code:
// Advanced Microcontroller-based Audio Workshop
//
// http://www.pjrc.com/store/audio_tutorial_kit.html
// https://hackaday.io/project/8292-microcontroller-audio-workshop-had-supercon-2015
// 
// Part 2-8: Oscillators

#include <Bounce.h>

Bounce button0 = Bounce(0, 15);
Bounce button1 = Bounce(1, 15);  // 15 = 15 ms debounce time
Bounce button2 = Bounce(2, 15);


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
//#include <SD.h>
#include <SerialFlash.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 5 
Adafruit_SSD1306 display(OLED_RESET);

#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
//#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=740,490
AudioSynthWaveformSine   sine1;          //xy=761,618
AudioSynthNoisePink      pink1;          //xy=764,692
AudioSynthWaveformSineModulated sine_fm1;       //xy=805,562
AudioMixer4              mixer1;         //xy=974,580
AudioEffectEnvelope      envelope1;      //xy=1003.7500190734863,731.7500114440918
AudioMixer4              mixer2;         //xy=1151.500015258789,653.2500076293945
AudioAnalyzeFFT1024      LeftFFT;      //xy=1355.0000190734863,682.5000114440918
AudioAnalyzeFFT1024      RightFFT;      //xy=1358,744
AudioOutputI2S           i2s1;           //xy=1393,540
AudioConnection          patchCord1(waveform1, 0, mixer1, 0);
AudioConnection          patchCord2(waveform1, sine_fm1);
AudioConnection          patchCord3(sine1, 0, mixer1, 2);
AudioConnection          patchCord4(pink1, 0, mixer1, 3);
AudioConnection          patchCord5(sine_fm1, 0, mixer1, 1);
AudioConnection          patchCord6(mixer1, 0, mixer2, 0);
AudioConnection          patchCord7(mixer1, envelope1);
AudioConnection          patchCord8(envelope1, 0, mixer2, 1);
AudioConnection          patchCord9(mixer2, 0, i2s1, 0);
AudioConnection          patchCord10(mixer2, 0, i2s1, 1);
AudioConnection          patchCord11(mixer2, LeftFFT);
AudioConnection          patchCord12(mixer2, RightFFT);
AudioControlSGTL5000     sgtl5000_1;     //xy=953,944
// GUItool: end automatically generated code



void setup() {
  Serial.begin(9600);
  AudioMemory(20);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.32);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  display.display();
  delay(2000);

  display.clearDisplay();

  display.setCursor(0, 0);
  
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  mixer1.gain(0, 0.75);
  mixer1.gain(1, 0.0);
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);
  mixer2.gain(0, 0.15);
  mixer2.gain(1, 0.0);
  mixer2.gain(2, 0.0);
  mixer2.gain(3, 0.0);
  waveform1.begin(WAVEFORM_SAWTOOTH);
  waveform1.amplitude(0.75);
  waveform1.frequency(50);
  waveform1.pulseWidth(0.15);
  sine_fm1.frequency(440);
  sine_fm1.amplitude(0.75);
  sine1.frequency(200);
  sine1.amplitude(0.75);
  pink1.amplitude(0.75);
  envelope1.attack(10);
  envelope1.hold(10);
  envelope1.decay(25);
  envelope1.sustain(0.4);
  envelope1.release(70);
}

int waveform_type = WAVEFORM_SAWTOOTH;
int mixer1_setting = 0;
int mixer2_setting = 0;
elapsedMillis timeout = 0;
bool mixer2_envelope = false;

unsigned long last_time = millis();
uint8_t overlayCounter = 0;
float lastLoopTime = 0;
uint16_t lastCPU = 0;
uint16_t lastMem = 0;

float leftBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

float RightBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};


void loop() {
  button0.update();
  button1.update();
  button2.update();

  display.setCursor(0, 0);

  // Left changes the type of control waveform
  if (button0.fallingEdge()) {
    Serial.print("Control waveform: ");
    if (waveform_type == WAVEFORM_SAWTOOTH) {
      waveform_type = WAVEFORM_SINE;
      Serial.println("Sine");
    } else if (waveform_type == WAVEFORM_SINE) {
      waveform_type = WAVEFORM_SQUARE;
      Serial.println("Square");
    } else if (waveform_type == WAVEFORM_SQUARE) {
      waveform_type = WAVEFORM_TRIANGLE;
      Serial.println("Triangle");
    } else if (waveform_type == WAVEFORM_TRIANGLE) {
      waveform_type = WAVEFORM_PULSE;
      Serial.println("Pulse");
    } else if (waveform_type == WAVEFORM_PULSE) {
      waveform_type = WAVEFORM_SAWTOOTH;
      Serial.println("Sawtooth");
    }
    waveform1.begin(waveform_type);
  }

  // middle button switch which source we hear from mixer1
  if (button1.fallingEdge()) {
    if (mixer1_setting == 0) {
      mixer1.gain(0, 0.75);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Control oscillator");
      mixer1_setting = 1;
    } else if (mixer1_setting == 1) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.75);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Frequency Modulated Oscillator");
      mixer1_setting = 2;
    } else if (mixer1_setting == 2) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.75);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Regular Sine Wave Oscillator");
      mixer1_setting = 3;
    } else if (mixer1_setting == 3) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.75);
      Serial.println("Mixer1: Pink Noise");
      mixer1_setting = 0;
    }
  }

  // Right button activates the envelope
  if (button2.fallingEdge()) {
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 1.0);
    mixer2_envelope = true;
    timeout = 0;
    envelope1.noteOn();
  }
  if (button2.risingEdge()) {
    envelope1.noteOff();
    timeout = 0;
  }

  // after 4 seconds of inactivity, go back to
  // steady listening intead of the envelope
  if (mixer2_envelope == true && timeout > 4000) {
    mixer2.gain(0, 0.15);
    mixer2.gain(1, 0.0);
    mixer2_envelope = false;
  }

  // use the knobs to adjust parameters
  float knob1 = (float)analogRead(A1) / 1023.0;
  float knob2 = (float)analogRead(A2) / 1023.0;
  float knob3 = (float)analogRead(A3) / 1023.0;
  waveform1.frequency(360 * knob2 + 0.25);
  sine_fm1.frequency(knob3 * 1500 + 50);
  sine1.frequency(knob3 * 1500 + 50);
//  Serial.println("knob1 = " + knob1);
  sgtl5000_1.volume(knob1); 


  float loopTime;
  int i;

  //calc loopTime
  unsigned long this_time = millis();
  if (this_time > last_time)
  {
    loopTime = (this_time - last_time);
  }
  last_time = this_time;

  //Update data every 20 frames for readability
  overlayCounter++;
  if (overlayCounter > 20)
  {
    lastLoopTime = loopTime;
    lastCPU = AudioProcessorUsageMax();
    AudioProcessorUsageMaxReset();
    lastMem = AudioMemoryUsageMax();
    AudioMemoryUsageMaxReset();

    overlayCounter = 0;
  }

  //Draw a frame
  display.clearDisplay();

  //Draw left bands
  for (i = 0; i < 40; i++)
  {
    if (leftBands[i] > 0.5) leftBands[i] = 0.25;
    display.drawLine(62 - i, 31, 62 - i, 31 - (int) (leftBands[i] * 1023), WHITE);
  }

  //Draw Right bands
  for (i = 0; i < 40; i++)
  {
    if (RightBands[i] > 0.5) RightBands[i] = 0.25;
    display.drawLine(65 + i, 31, 65 + i, 31 - (int) (RightBands[i] * 1023), WHITE);
  }

    // text display tests
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
//  display.println("Hello, world!");

  //Overlay info
  //  loop time
  display.setCursor(0, 0);
  display.print("Loop=");
  display.print((uint8_t)lastLoopTime);
  display.print("ms");
  //  Teensy Audio info
  display.setCursor(83, 0);
  display.print("cpu=");
  display.print(lastCPU);
  display.setCursor(91, 8);
  display.print("mem=");
  display.print(lastMem);
  //  L/R letters
  display.setCursor(15, 24);
  display.print("L");
  display.setCursor(108, 24);
  display.print("R");

  if (LeftFFT.available()) {
    // each time new FFT data is available
    for (i = 0; i < 40; i++) {
      leftBands[i] = LeftFFT.read(i);
    }
  }
  if (RightFFT.available()) {
    // each time new FFT data is available
    for (i = 0; i < 40; i++) {
      RightBands[i] = RightFFT.read(i);
    }
  }

  display.display();

  
}
 
This OLEDFeatherWing has a SSD1306 display. The current code just blocks the execution of loop() while the display is being refreshed. Audio is not affected, because it runs on interrupt.
It may get better, if you execute the "//Draw a frame"-section maybe 4 times a second. Ask for millis(). Or at least, move this part up into the if(){} of "//Update data every 20 frames...", so the display is refreshed only every 20th run.

Also, you are drawing first the frame and then check for new data. I'd first check for new data and draw a frame only if new data are available.
 
Seeing what FrankB saw - this is updating the display each time - but FFT data only updated when .available.

T_3.6 loop() should run up to a MILLION times a second - allowing USB Host processing in good time.

The display update is perhaps 56 ms and that is limiting the loop cycling to 17 times per second.

So YES this " // TODO: should this be higher?" code should be higher and the data arrays should only update with new data .available and the display should be updated:
Code:
  if (LeftFFT.available()) {
    // each time new FFT data is available
    for (i = 0; i < 40; i++) {
      leftBands[i] = LeftFFT.read(i);
    }
  }
  if (RightFFT.available()) {
    // each time new FFT data is available
    for (i = 0; i < 40; i++) {
      RightBands[i] = RightFFT.read(i);
    }
  }

  display.display();

This is the array update that is only needed when new data arrives:
Code:
  //Draw a frame
  display.clearDisplay();

  //Draw left bands
  for (i = 0; i < 40; i++)
  {
    if (leftBands[i] > 0.5) leftBands[i] = 0.25;
    display.drawLine(62 - i, 31, 62 - i, 31 - (int) (leftBands[i] * 1023), WHITE);
  }

  //Draw Right bands
  for (i = 0; i < 40; i++)
  {
    if (RightBands[i] > 0.5) RightBands[i] = 0.25;
    display.drawLine(65 + i, 31, 65 + i, 31 - (int) (RightBands[i] * 1023), WHITE);
  }

THough the FFT update it seems updates faster than 17 times per second - reading the @Deleted User crosspost - an overt limit on display update should perhaps be done - even dropping to 10X/second will allow many more loop passes for processing.
 
Ok, I think I'm starting to get it - thanks so much to you all for this help! It's late here so I'll make some changes in the morning and report back :)
 
From Audio Tutorial :: ...\hardware\teensy\avr\libraries\Audio\examples\Tutorial\Part_3_03_TFT_Display\Part_3_03_TFT_Display.ino

This loop() code shows limiting the display update. To start try " if (msecs > 100) { " below. This line "msecs = 0;" is the one that works to control the timing reset after in this case the Peak#'s are .available - you might do the same with FFT's.
Code:
[U]elapsedMillis msecs;[/U]

void loop() {
  if (playSdWav1.isPlaying() == false) {
    Serial.println("Start playing");
    //playSdWav1.play("SDTEST1.WAV");
    //playSdWav1.play("SDTEST2.WAV");
    playSdWav1.play("SDTEST3.WAV");
    //playSdWav1.play("SDTEST4.WAV");
    delay(10); // wait for library to parse WAV info
  }
  
[B][U]  if (msecs > 15) {[/U][/B]
    if (peak1.available() && peak2.available()) {
      [B][U]msecs = 0;[/U][/B]
      float leftNumber = peak1.read();
      float rightNumber = peak2.read();
      Serial.print(leftNumber);
      Serial.print(", ");
      Serial.print(rightNumber);
      Serial.println();

      // draw the verticle bars
//…

      // a smarter approach would redraw only the changed portion...

      // draw numbers underneath each bar
//…
    }
  }
}
 
It works!

Code below. It didn't seem to make a noticeable difference whether I put the FFT.available code inside or outside the loop that runs every 100ms; either way the loop now runs somewhere ~1ms :)

Code:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Audio.h>
#include <Bounce.h>
// #include <SD.h>
#include <SerialFlash.h>
#include <SPI.h>
#include "USBHost_t36.h"
#include <Wire.h>

Bounce button0 = Bounce(0, 15);
Bounce button1 = Bounce(1, 15);  // 15 = 15 ms debounce time
Bounce button2 = Bounce(2, 15);

#define OLED_RESET 5 
Adafruit_SSD1306 display(OLED_RESET);

// stuff for Adafruit logo
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=740,490
AudioSynthWaveformSine   sine1;          //xy=761,618
AudioSynthNoisePink      pink1;          //xy=764,692
AudioSynthWaveformSineModulated sine_fm1;       //xy=805,562
AudioMixer4              mixer1;         //xy=974,580
AudioEffectEnvelope      envelope1;      //xy=1003.7500190734863,731.7500114440918
AudioMixer4              mixer2;         //xy=1151.500015258789,653.2500076293945
AudioAnalyzeFFT1024      LeftFFT;      //xy=1355.0000190734863,682.5000114440918
AudioAnalyzeFFT1024      RightFFT;      //xy=1358,744
AudioOutputI2S           i2s1;           //xy=1393,540
AudioConnection          patchCord1(waveform1, 0, mixer1, 0);
AudioConnection          patchCord2(waveform1, sine_fm1);
AudioConnection          patchCord3(sine1, 0, mixer1, 2);
AudioConnection          patchCord4(pink1, 0, mixer1, 3);
AudioConnection          patchCord5(sine_fm1, 0, mixer1, 1);
AudioConnection          patchCord6(mixer1, 0, mixer2, 0);
AudioConnection          patchCord7(mixer1, envelope1);
AudioConnection          patchCord8(envelope1, 0, mixer2, 1);
AudioConnection          patchCord9(mixer2, 0, i2s1, 0);
AudioConnection          patchCord10(mixer2, 0, i2s1, 1);
AudioConnection          patchCord11(mixer2, LeftFFT);
AudioConnection          patchCord12(mixer2, RightFFT);
AudioControlSGTL5000     sgtl5000_1;     //xy=953,944
// GUItool: end automatically generated code

// USB Setup
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
MIDIDevice midi1(myusb);

float knob1 = 0;
float knob2 = 0;
float knob3 = 0;

void setup() {
  Serial.begin(9600);
  AudioMemory(20);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.32);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  display.display();
  delay(2000);

  display.clearDisplay();

  display.setCursor(0, 0);
  
  // SET UP PINS
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);

  // SET UP MIXER
  mixer1.gain(0, 0.75);
  mixer1.gain(1, 0.0);
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);
  mixer2.gain(0, 0.15);
  mixer2.gain(1, 0.0);
  mixer2.gain(2, 0.0);
  mixer2.gain(3, 0.0);

  // SET UP SYNTH
  waveform1.begin(WAVEFORM_SAWTOOTH);
  waveform1.amplitude(0.75);
  waveform1.frequency(50);
  waveform1.pulseWidth(0.15);
  sine_fm1.frequency(440);
  sine_fm1.amplitude(0.75);
  sine1.frequency(200);
  sine1.amplitude(0.75);
  pink1.amplitude(0.75);
  envelope1.attack(10);
  envelope1.hold(10);
  envelope1.decay(25);
  envelope1.sustain(0.4);
  envelope1.release(70);
  
  // SET UP USB
  myusb.begin();
  midi1.setHandleNoteOff(OnNoteOff);
  midi1.setHandleNoteOn(OnNoteOn);
  midi1.setHandleControlChange(OnControlChange);
}

int waveform_type = WAVEFORM_SAWTOOTH;
int mixer1_setting = 0;
int mixer2_setting = 0;
elapsedMillis timeout = 0;
bool mixer2_envelope = false;

unsigned long last_time = millis();
uint8_t overlayCounter = 0;
float lastLoopTime = 0;
uint16_t lastCPU = 0;
uint16_t lastMem = 0;

// Initialise FFT array
float leftBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

float RightBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

elapsedMillis msecs;

void loop() {
  button0.update();
  button1.update();
  button2.update();

  myusb.Task();
  midi1.read();

  display.setCursor(0, 0);

  // Left changes the type of control waveform
  if (button0.fallingEdge()) {
    Serial.print("Control waveform: ");
    if (waveform_type == WAVEFORM_SAWTOOTH) {
      waveform_type = WAVEFORM_SINE;
      Serial.println("Sine");
    } else if (waveform_type == WAVEFORM_SINE) {
      waveform_type = WAVEFORM_SQUARE;
      Serial.println("Square");
    } else if (waveform_type == WAVEFORM_SQUARE) {
      waveform_type = WAVEFORM_TRIANGLE;
      Serial.println("Triangle");
    } else if (waveform_type == WAVEFORM_TRIANGLE) {
      waveform_type = WAVEFORM_PULSE;
      Serial.println("Pulse");
    } else if (waveform_type == WAVEFORM_PULSE) {
      waveform_type = WAVEFORM_SAWTOOTH;
      Serial.println("Sawtooth");
    }
    waveform1.begin(waveform_type);
  }

  // Check middle button
  // If pressed, Cycle sequentially through various mixer settings
  if (button1.fallingEdge()) {
    if (mixer1_setting == 0) {
      mixer1.gain(0, 0.75);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Control oscillator");
      mixer1_setting = 1;
    } else if (mixer1_setting == 1) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.75);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Frequency Modulated Oscillator");
      mixer1_setting = 2;
    } else if (mixer1_setting == 2) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.75);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Regular Sine Wave Oscillator");
      mixer1_setting = 3;
    } else if (mixer1_setting == 3) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.75);
      Serial.println("Mixer1: Pink Noise");
      mixer1_setting = 0;
    }
  }

  // Check right button
  // If pressed, activate the envelope
  if (button2.fallingEdge()) {
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 1.0);
    mixer2_envelope = true;
    timeout = 0;
    envelope1.noteOn();
  }
  if (button2.risingEdge()) {
    envelope1.noteOff();
    timeout = 0;
  }

  // after 4 seconds of inactivity, go back to
  // steady listening intead of the envelope
  if (mixer2_envelope == true && timeout > 4000) {
    mixer2.gain(0, 0.15);
    mixer2.gain(1, 0.0);
    mixer2_envelope = false;
  }

  // use the knobs to adjust parameters
  // float knob2 = (float)analogRead(A2) / 1023.0;
  // float knob3 = (float)analogRead(A3) / 1023.0;

  //  waveform1.frequency(360 * knob2 + 0.25);
  //  sine_fm1.frequency(knob3 * 1500 + 50);
  //  sine1.frequency(knob3 * 1500 + 50);

  //  Serial.print("knob1 = ");
  //  Serial.println(knob1);
  //  Serial.print("\tknob2 = ");
  //  Serial.print(knob2);
  //  Serial.print("\tknob3 = ");
  //  Serial.println(knob3);

  knob1 = (float)analogRead(A1) / 1023.0;

  sgtl5000_1.volume(knob1); 
  waveform1.frequency(knob2);
  sine_fm1.frequency(knob3);
  sine1.frequency(knob3);

  // Set up Overlay
  float loopTime;
  int i;

  //calc loopTime
  unsigned long this_time = millis();
  if (this_time > last_time)
  {
    loopTime = (this_time - last_time);
  }
  last_time = this_time;

  //Update data every 20 frames for readability
  overlayCounter++;
  if (overlayCounter > 20)
  {
    lastLoopTime = loopTime;
    lastCPU = AudioProcessorUsageMax();
    AudioProcessorUsageMaxReset();
    lastMem = AudioMemoryUsageMax();
    AudioMemoryUsageMaxReset();

    overlayCounter = 0;
  }

  //Draw a frame
  display.clearDisplay();

    if (LeftFFT.available()) {
      for (i = 0; i < 40; i++) {
        leftBands[i] = LeftFFT.read(i);
      }
    }
    if (RightFFT.available()) {
      for (i = 0; i < 40; i++) {
        RightBands[i] = RightFFT.read(i);
      }
    }


  // runs every 100ms
  if (msecs > 100) {
    msecs = 0;


    //Draw left FFT bands
    for (i = 0; i < 40; i++)
    {
      if (leftBands[i] > 0.5) leftBands[i] = 0.25;
      display.drawLine(62 - i, 31, 62 - i, 31 - (int) (leftBands[i] * 1023), WHITE);
    }

    //Draw Right FFT bands
    for (i = 0; i < 40; i++)
    {
      if (RightBands[i] > 0.5) RightBands[i] = 0.25;
      display.drawLine(65 + i, 31, 65 + i, 31 - (int) (RightBands[i] * 1023), WHITE);
    }

    // text display tests
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    //  display.println("Hello, world!");

    //Overlay info
    //  loop time
    display.setCursor(0, 0);
    display.print("Loop=");
    display.print((uint8_t)lastLoopTime);
    display.print("ms");
    //  Teensy Audio info
    display.setCursor(83, 0);
    display.print("cpu=");
    display.print(lastCPU);
    display.setCursor(91, 8);
    display.print("mem=");
    display.print(lastMem);
    //  L/R letters
    display.setCursor(15, 24);
    display.print("L");
    display.setCursor(108, 24);
    display.print("R");

    display.display();
  } // end section running every 100ms
}

void OnNoteOn(byte channel, byte note, byte velocity)
{
  Serial.print("Note On, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  Serial.print(", velocity=");
  Serial.print(velocity);
  Serial.println();
}

void OnNoteOff(byte channel, byte note, byte velocity)
{
  Serial.print("Note Off, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  //Serial.print(", velocity=");
  //Serial.print(velocity);
  Serial.println();
}

void OnControlChange(byte channel, byte control, byte value)
{
//  Commented these in case they were causing a delay:

//  Serial.print("Control Change, ch=");
//  Serial.print(channel);
//  Serial.print(", control=");
//  Serial.print(control);
//  Serial.print(", value=");
//  Serial.print(value);
//  Serial.println();
  int mappedValue;
    switch (control) {
//  reverted this to faster analog pot input due to nasty volume
//  surprises :(

//  case 8: //volume
//    mappedValue = constrain(value, 1,100);
//    Serial.print("Value: ");
//    Serial.println(mappedValue);
//    knob1 = mappedValue;
//    break;
  case 23: //knob2
    mappedValue = map(value, 1,128, 1, 356);
  //  Serial.println(mappedValue);
    knob2 = mappedValue;
    break;
  case 11: //knob3
    mappedValue = map(value, 1,128, 60, 1500);
//    Serial.println(mappedValue);
    knob3 = mappedValue;
    break;
  default:
    break;
  }

}
 
I was wondering whether display.display() is the part that actually writes out to the display, and display.drawLine(x1, y1, x2, y2) just stores values to be drawn in a buffer/array somewhere.

If so I was thinking the code could be simplified (although I understand not necessarily optimised) by doing:

Code:
    if (LeftFFT.available()) {
      for (i = 0; i < 40; i++) {
        leftBands[i] = LeftFFT.read(i);
        if (leftBands[i] > 0.5) leftBands[i] = 0.25;
        display.drawLine(62 - i, 31, 62 - i, 31 - (int) (leftBands[i] * 1023), WHITE);
      }
    }
    if (RightFFT.available()) {
      for (i = 0; i < 40; i++) {
        RightBands[i] = RightFFT.read(i);
        if (RightBands[i] > 0.5) RightBands[i] = 0.25;
        display.drawLine(65 + i, 31, 65 + i, 31 - (int) (RightBands[i] * 1023), WHITE);
      }
    }

This seems to work, so I went further and tried to implement this comment in the TFT code that @defragster posted:

Code:
 // a smarter approach would redraw only the changed portion...
However my attempt at this only seems to slow down drawing to the display even further (doesn't affect audio). I suspect display.display() just dumps out the same amount of data regardless of what pixels have changed. I'll post that code below FWIW.

I'm curious, is the slowness with this Adafruit display due to it using I2C, or are OLEDs just slow to write out to in general? I'd like to try this with a nice large, square display but don't want to slow the sketch down further. Would appreciate any display recommendations!

Here's the code that attempts to only update FFT bands that have changed, but seems to just slow down the display:

Code:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Audio.h>
#include <Bounce.h>
// #include <SD.h>
#include <SerialFlash.h>
#include <SPI.h>
#include "USBHost_t36.h"
#include <Wire.h>

Bounce button0 = Bounce(0, 15);
Bounce button1 = Bounce(1, 15);  // 15 = 15 ms debounce time
Bounce button2 = Bounce(2, 15);

#define OLED_RESET 5 
Adafruit_SSD1306 display(OLED_RESET);

// stuff for Adafruit logo
#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
// #include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform1;      //xy=740,490
AudioSynthWaveformSine   sine1;          //xy=761,618
AudioSynthNoisePink      pink1;          //xy=764,692
AudioSynthWaveformSineModulated sine_fm1;       //xy=805,562
AudioMixer4              mixer1;         //xy=974,580
AudioEffectEnvelope      envelope1;      //xy=1003.7500190734863,731.7500114440918
AudioMixer4              mixer2;         //xy=1151.500015258789,653.2500076293945
AudioAnalyzeFFT1024      LeftFFT;      //xy=1355.0000190734863,682.5000114440918
AudioAnalyzeFFT1024      RightFFT;      //xy=1358,744
AudioOutputI2S           i2s1;           //xy=1393,540
AudioConnection          patchCord1(waveform1, 0, mixer1, 0);
AudioConnection          patchCord2(waveform1, sine_fm1);
AudioConnection          patchCord3(sine1, 0, mixer1, 2);
AudioConnection          patchCord4(pink1, 0, mixer1, 3);
AudioConnection          patchCord5(sine_fm1, 0, mixer1, 1);
AudioConnection          patchCord6(mixer1, 0, mixer2, 0);
AudioConnection          patchCord7(mixer1, envelope1);
AudioConnection          patchCord8(envelope1, 0, mixer2, 1);
AudioConnection          patchCord9(mixer2, 0, i2s1, 0);
AudioConnection          patchCord10(mixer2, 0, i2s1, 1);
AudioConnection          patchCord11(mixer2, LeftFFT);
AudioConnection          patchCord12(mixer2, RightFFT);
AudioControlSGTL5000     sgtl5000_1;     //xy=953,944
// GUItool: end automatically generated code

// USB Setup
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHub hub3(myusb);
MIDIDevice midi1(myusb);

float knob1 = 0;
float knob2 = 0;
float knob3 = 0;

// Initialise FFT array
float leftBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

float rightBands[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

// Initialise FFT array
float leftBandsOld[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

float rightBandsOld[40] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

void setup() {
  Serial.begin(9600);
  AudioMemory(20);
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.32);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  display.display();
  delay(2000);

  display.clearDisplay();

  display.setCursor(0, 0);
  
  // SET UP PINS
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);

  // SET UP MIXER
  mixer1.gain(0, 0.75);
  mixer1.gain(1, 0.0);
  mixer1.gain(2, 0.0);
  mixer1.gain(3, 0.0);
  mixer2.gain(0, 0.15);
  mixer2.gain(1, 0.0);
  mixer2.gain(2, 0.0);
  mixer2.gain(3, 0.0);

  // SET UP SYNTH
  waveform1.begin(WAVEFORM_SAWTOOTH);
  waveform1.amplitude(0.75);
  waveform1.frequency(50);
  waveform1.pulseWidth(0.15);
  sine_fm1.frequency(440);
  sine_fm1.amplitude(0.75);
  sine1.frequency(200);
  sine1.amplitude(0.75);
  pink1.amplitude(0.75);
  envelope1.attack(10);
  envelope1.hold(10);
  envelope1.decay(25);
  envelope1.sustain(0.4);
  envelope1.release(70);
  
  // SET UP USB
  myusb.begin();
  midi1.setHandleNoteOff(OnNoteOff);
  midi1.setHandleNoteOn(OnNoteOn);
  midi1.setHandleControlChange(OnControlChange);
}

int waveform_type = WAVEFORM_SAWTOOTH;
int mixer1_setting = 0;
int mixer2_setting = 0;
elapsedMillis timeout = 0;
bool mixer2_envelope = false;

unsigned long last_time = millis();
uint8_t overlayCounter = 0;
float lastLoopTime = 0;
uint16_t lastCPU = 0;
uint16_t lastMem = 0;


elapsedMillis msecs;

void loop() {
  button0.update();
  button1.update();
  button2.update();

  myusb.Task();
  midi1.read();

  display.setCursor(0, 0);

  // Left changes the type of control waveform
  if (button0.fallingEdge()) {
    Serial.print("Control waveform: ");
    if (waveform_type == WAVEFORM_SAWTOOTH) {
      waveform_type = WAVEFORM_SINE;
      Serial.println("Sine");
    } else if (waveform_type == WAVEFORM_SINE) {
      waveform_type = WAVEFORM_SQUARE;
      Serial.println("Square");
    } else if (waveform_type == WAVEFORM_SQUARE) {
      waveform_type = WAVEFORM_TRIANGLE;
      Serial.println("Triangle");
    } else if (waveform_type == WAVEFORM_TRIANGLE) {
      waveform_type = WAVEFORM_PULSE;
      Serial.println("Pulse");
    } else if (waveform_type == WAVEFORM_PULSE) {
      waveform_type = WAVEFORM_SAWTOOTH;
      Serial.println("Sawtooth");
    }
    waveform1.begin(waveform_type);
  }

  // Check middle button
  // If pressed, Cycle sequentially through various mixer settings
  if (button1.fallingEdge()) {
    if (mixer1_setting == 0) {
      mixer1.gain(0, 0.75);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Control oscillator");
      mixer1_setting = 1;
    } else if (mixer1_setting == 1) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.75);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Frequency Modulated Oscillator");
      mixer1_setting = 2;
    } else if (mixer1_setting == 2) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.75);
      mixer1.gain(3, 0.0);
      Serial.println("Mixer1: Regular Sine Wave Oscillator");
      mixer1_setting = 3;
    } else if (mixer1_setting == 3) {
      mixer1.gain(0, 0.0);
      mixer1.gain(1, 0.0);
      mixer1.gain(2, 0.0);
      mixer1.gain(3, 0.75);
      Serial.println("Mixer1: Pink Noise");
      mixer1_setting = 0;
    }
  }

  // Check right button
  // If pressed, activate the envelope
  if (button2.fallingEdge()) {
    mixer2.gain(0, 0.0);
    mixer2.gain(1, 1.0);
    mixer2_envelope = true;
    timeout = 0;
    envelope1.noteOn();
  }
  if (button2.risingEdge()) {
    envelope1.noteOff();
    timeout = 0;
  }

  // after 4 seconds of inactivity, go back to
  // steady listening intead of the envelope
  if (mixer2_envelope == true && timeout > 4000) {
    mixer2.gain(0, 0.15);
    mixer2.gain(1, 0.0);
    mixer2_envelope = false;
  }

  knob1 = (float)analogRead(A1) / 1023.0;

  sgtl5000_1.volume(knob1); 
  waveform1.frequency(knob2);
  sine_fm1.frequency(knob3);
  sine1.frequency(knob3);

  // Set up Overlay
  float loopTime;
  int i;

  //calc loopTime
  unsigned long this_time = millis();
  if (this_time > last_time)
  {
    loopTime = (this_time - last_time);
  }
  last_time = this_time;

  //Update data every 20 frames for readability
  overlayCounter++;
  if (overlayCounter > 20)
  {
    lastLoopTime = loopTime;
    lastCPU = AudioProcessorUsageMax();
    AudioProcessorUsageMaxReset();
    lastMem = AudioMemoryUsageMax();
    AudioMemoryUsageMaxReset();

    overlayCounter = 0;
  }

  //Draw a frame
  display.clearDisplay();

  // runs every 100ms
  if (msecs > 100) {

    msecs = 0;

    if (LeftFFT.available()) {
      for (i = 0; i < 40; i++) {
        leftBands[i] = LeftFFT.read(i);
        if (leftBands[i] > 0.5) leftBands[i] = 0.25;
        if (leftBands[i] == leftBandsOld[i]){
          return; // don't draw anything if band value hasn't changed
        } else {
        display.drawLine(62 - i, 31, 62 - i, 31 - (int) (leftBands[i] * 1023), WHITE);
        leftBandsOld[i] = leftBands[i]; // store changed values in leftBandsOld
       }
      }
    }
    if (RightFFT.available()) {
      for (i = 0; i < 40; i++) {
        rightBands[i] = RightFFT.read(i);
        if (rightBands[i] > 0.5) rightBands[i] = 0.25;
        if (rightBands[i] == rightBandsOld[i]){
          return;
        } else {
        display.drawLine(65 + i, 31, 65 + i, 31 - (int) (rightBands[i] * 1023), WHITE);
        rightBandsOld[i] = rightBands[i];
       }
      }
    }

    // text display tests
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    //  display.println("Hello, world!");

    //Overlay info
    //  loop time
    display.setCursor(0, 0);
    display.print("Loop=");
    display.print((uint8_t)lastLoopTime);
    display.print("ms");
    //  Teensy Audio info
    display.setCursor(83, 0);
    display.print("cpu=");
    display.print(lastCPU);
    display.setCursor(91, 8);
    display.print("mem=");
    display.print(lastMem);
    //  L/R letters
    display.setCursor(15, 24);
    display.print("L");
    display.setCursor(108, 24);
    display.print("R");

    display.display();
  } // end section running every 100ms
}

void OnNoteOn(byte channel, byte note, byte velocity)
{
  Serial.print("Note On, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  Serial.print(", velocity=");
  Serial.print(velocity);
  Serial.println();
}

void OnNoteOff(byte channel, byte note, byte velocity)
{
  Serial.print("Note Off, ch=");
  Serial.print(channel);
  Serial.print(", note=");
  Serial.print(note);
  //Serial.print(", velocity=");
  //Serial.print(velocity);
  Serial.println();
}

void OnControlChange(byte channel, byte control, byte value)
{
//  Commented these in case they were causing a delay:

//  Serial.print("Control Change, ch=");
//  Serial.print(channel);
//  Serial.print(", control=");
//  Serial.print(control);
//  Serial.print(", value=");
//  Serial.print(value);
//  Serial.println();
  int mappedValue;
    switch (control) {
//  reverted this to faster analog pot input due to nasty volume
//  surprises :(

//  case 8: //volume
//    mappedValue = constrain(value, 1,100);
//    Serial.print("Value: ");
//    Serial.println(mappedValue);
//    knob1 = mappedValue;
//    break;
  case 23: //knob2
    mappedValue = map(value, 1,128, 1, 356);
  //  Serial.println(mappedValue);
    knob2 = mappedValue;
    break;
  case 11: //knob3
    mappedValue = map(value, 1,128, 60, 1500);
//    Serial.println(mappedValue);
    knob3 = mappedValue;
    break;
  default:
    break;
  }

}
 
Status
Not open for further replies.
Back
Top