// 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();
}