Hi,
I've been having a great time with my Teensy 3.6 / Audio Shield and sending FFT data to an OLED display.
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!
I've been having a great time with my Teensy 3.6 / Audio Shield and sending FFT data to an OLED display.
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;
}
}