🔍

The Keyboard Synthesizer aka another minimalistic TEENSY synth

The TEENSY microcontroller is a special device because of it’s very powerful lib written for it – the Teensy Audio Library which makes it possible to create a respectable variety of synthetic sound with a very minimal effort of electronics and coding.

VERSION II WITH KEYBOARD AND NOKIA 5110

THis version uses an old PS2 Keyboard as input device


#include <Arduino.h>
#include <PS2Keyboard.h>
#include <audio_lib.h>


// ---------------------------------
// Include the library
#include <NOKIA5110_TEXT.h>

// LCD Nokia 5110 pinout left to right
// RST / CE / DC / DIN / CLK / VCC/ LIGHT / GND

#define RST 14 // Reset pin
#define CE 15 // Chip enable
#define DC 16 // data or command
#define DIN 17 // Serial Data input
#define CLK 18 // Serial clock

// Create an LCD object
NOKIA5110_TEXT mylcd(RST, CE, DC, DIN, CLK);

#define inverse false
#define contrast 0xBF // default is 0xBF set in LCDinit, Try 0xB1 - 0xBF if your display is too dark/dim
#define bias 0x13 // LCD bias mode 1:48: Try 0x12 , 0x13 or 0x14

#define display_LED_PIN  19


const int DataPin = 8;
const int IRQpin =  5;

PS2Keyboard keyboard;

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

// GUItool: begin automatically generated code
AudioSynthWaveform       waveform2; //xy=731.0000114440918,560.0000085830688
AudioSynthWaveform       waveform3; //xy=737.0000114440918,641.0000095367432
AudioSynthWaveform       waveform1;      //xy=744.0000076293945,494.00000762939453
AudioMixer4              mixer1;         //xy=1019.0000152587891,568.0000076293945
AudioAmplifier           amp1;           //xy=1177.000015258789,566.0000085830688
AudioEffectEnvelope      envelope1;      //xy=1382.0000228881836,580.0000076293945
AudioEffectFreeverb      freeverb1;      //xy=1593.0000228881836,584.0000085830688
AudioOutputAnalog        dac1;           //xy=1840.0000228881836,572.0000076293945
AudioConnection          patchCord1(waveform2, 0, mixer1, 1);
AudioConnection          patchCord2(waveform3, 0, mixer1, 2);
AudioConnection          patchCord3(waveform1, 0, mixer1, 0);
AudioConnection          patchCord4(mixer1, amp1);
AudioConnection          patchCord5(amp1, envelope1);
AudioConnection          patchCord6(envelope1, freeverb1);
AudioConnection          patchCord7(freeverb1, dac1);
// GUItool: end automatically generated code



int f1=222;
int f2=222;
int f3=222;

int octave = -24;

int note_cnt = 0;

byte size_of_keyboard_buffer = 12;

// ---------------------------------------


void oscPlay(byte note) {
  waveform1.frequency(noteFreqs[note]);
  waveform2.frequency(noteFreqs[note]);
  waveform3.frequency(noteFreqs[note]);
  float velo = 111;
  waveform1.amplitude(velo);
  waveform2.amplitude(velo);
  waveform3.amplitude(velo);
  //pink1.amplitude(velo);
    envelope1.noteOn(); 

    
}

void oscStop() {
  waveform1.amplitude(0.0);
  waveform2.amplitude(0.0);
   waveform3.amplitude(0.0);
  //pink1.amplitude(0.0);
   envelope1.noteOff(); 
}


void keyBuff(byte note, bool playNote) {
  static byte buff[BUFFER];
  static byte buffSize = 0;

  // Add Note
  if (playNote == true && (buffSize < BUFFER) ) {
     oscPlay(note);
    buff[buffSize] = note;
    buffSize++;
    return;
  }

  // Remove Note
  else if (playNote == false && buffSize != 0) {
    for (byte found = 0; found < buffSize; found++) {
      if (buff[found] == note) {
        for (byte gap = found; gap < (buffSize - 1); gap++) {
          buff[gap] = buff[gap + 1];
        }
        buffSize--;
        buff[buffSize] = 255;
        if (buffSize != 0) {
           //oscPlay(buff[buffSize - 1]);
          return;
        }
        else {
           oscStop();
          return;
        }
      }
    }
  }
}







void playNote(int _noteid){
 
    keyBuff(_noteid, true);
 
}


// ---------------------------------------

 



void setup() {


   mylcd.LCDInit(inverse, contrast, bias); // init  the LCD
 // mylcd.LCDClear(0x00); // Clear whole screen
  mylcd.LCDFont(LCDFont_Default); // Set the font

  //mylcd.LCD
 mylcd.LCDgotoXY(0, 0); // (go to (X , Y) (0-84 columns, 0-5 blocks) top left corner
  mylcd.LCDString("~ INIT SYNTH ~"); //print
  
  pinMode(display_LED_PIN,OUTPUT);
  digitalWrite(display_LED_PIN,HIGH);

  delay(100);

  //Serial.println("TRY Keyboard Test:");
  keyboard.begin(DataPin, IRQpin, PS2Keymap_German);

   Serial.begin(9600);
  Serial.println("Keyboard Test:");

   AudioMemory(30);
 waveform1.begin(WAVEFORM_SAWTOOTH);
  waveform2.begin(WAVEFORM_SAWTOOTH);
  waveform3.begin(WAVEFORM_SQUARE);
 
  waveform1.amplitude(.83);
  waveform2.amplitude(.83);
  waveform3.amplitude(.83);
 
  waveform1.frequency(f1);
  waveform2.frequency(f2);
  waveform3.frequency(f3);
 
  waveform1.pulseWidth(.2);
  waveform2.pulseWidth(.33);
  waveform3.pulseWidth(.44);


  envelope1.attack(9.2);
  envelope1.hold(122.1);
  envelope1.decay(131.4);
   envelope1.sustain(0.8);
  envelope1.release(424.5);

mixer1.gain(0,.99);
mixer1.gain(1,.99);
mixer1.gain(2,.99);

amp1.gain(.9);

 freeverb1.roomsize(.5);
 freeverb1.damping(.5);

 
}


bool apressed = false;

int keys[12]; 

int triggval = 10;

int keys_vals[12] = {60,62,64,65,67,69,71,0,0,0,0};

void loop() {

apressed = false;  
  if (keyboard.available()) {


    
     
    // read the next key
    char c = keyboard.read();


    if(c == 'a'){playNote(keys_vals[0]+octave); keys[0]=triggval ;}// C
    if(c == 's'){playNote(keys_vals[1]+octave); keys[1]=triggval ;}// D
    if(c == 'd'){playNote(keys_vals[2]+octave); keys[2]=triggval ;} // E
    if(c == 'f'){playNote(keys_vals[3]+octave); keys[3]=triggval ;} // F
    if(c == 'g'){playNote(keys_vals[4]+octave); keys[4]=triggval ;} // G
    if(c == 'h'){playNote(keys_vals[5]+octave); keys[5]=triggval ;} // A
    if(c == 'j'){playNote(keys_vals[6]+octave); keys[6]=triggval ;} // H

    if(c == '1'){ octave += 12;}//  next octave
    if(c == '2'){ octave -= 12;}//  next octave
   
    //note_cnt = 0;

     
    /*
    // check for some of the special keys
    if (c == PS2_ENTER) {
      Serial.println();
    } else if (c == PS2_TAB) {
      Serial.print("[Tab]");
    } else if (c == PS2_ESC) {
      Serial.print("[ESC]");
    } else if (c == PS2_PAGEDOWN) {
      Serial.print("[PgDn]");
    } else if (c == PS2_PAGEUP) {
      Serial.print("[PgUp]");
    } else if (c == PS2_LEFTARROW) {
      Serial.print("[Left]");
    } else if (c == PS2_RIGHTARROW) {
      Serial.print("[Right]");
    } else if (c == PS2_UPARROW) {
      Serial.print("[Up]");
    } else if (c == PS2_DOWNARROW) {
      Serial.print("[Down]");
    } else if (c == PS2_DELETE) {
      Serial.print("[Del]");
    } else {
      
      // otherwise, just print all normal characters
      
    }

    */

   //Serial.print(c);
  }

 
mylcd.LCDClear(0x00); // Clear whole screen

mylcd.LCDgotoXY(20, 60); 
mylcd.LCDString("PLAYMODE");
 for(int i=0;i<12;i++){

    
    if(keys[i]<=1){
      keyBuff(keys_vals[i]+octave, false);
     // Serial.print("turnoff: ");
     // Serial.println(keys_vals[i]+octave);
    }      

    if(keys[i]>0){

      //mylcd.LCDgotoXY(i, 2);
    //  mylcd.LCDSetPixel(i*4,10);

       mylcd.LCDgotoXY(i*10, 0); // (go to (X , Y) (0-84 columns, 0-5 blocks) top left corner
       //mylcd.LCDString("o"); //print
       mylcd.LCDCustomChar(FACE, sizeof(FACE) / sizeof(unsigned char), LCDPadding_None, true);
           
      keys[i]--;
    }

 }

 // 


 
  delay(10);
}






INITAL VERSION WITH RGB RING

#include <Arduino.h>

#include <trxy_btn.h>
#include <Adafruit_NeoPixel.h>


// -------------------------------------------
// ------------------------------------------


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

 
AudioSynthWaveform       waveform1;      //xy=755.0000114440918,513.0000076293945
AudioSynthWaveform       waveform3; //xy=759,647
AudioSynthWaveform       waveform2; //xy=760.0000076293945,574.0000095367432
AudioMixer4              mixer1;         //xy=1019.0000152587891,586.0000095367432
AudioOutputAnalog        dac1;           //xy=1255.000015258789,567.0000095367432
AudioConnection          patchCord1(waveform1, 0, mixer1, 0);
AudioConnection          patchCord2(waveform3, 0, mixer1, 2);
AudioConnection          patchCord3(waveform2, 0, mixer1, 1);
AudioConnection          patchCord4(mixer1, dac1);
 



// -------------------------------------------
// ------------------------------------------


#define PIN  6//LED RING PIN

 
Adafruit_NeoPixel pixels(12, PIN, NEO_GRB + NEO_KHZ800);

#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>

#define ENC1 0
#define ENC2 1
 

aBTN MAINBTN(4,false);

aBTN PLAYBTN(7,false);
 
Encoder myEnc(ENC1, ENC2);
int led = 13;


int f1=444;
int f2=444;
int f3=444;

//   avoid using pins with LEDs attached

void setup() {


  AudioMemory(20);
  waveform1.begin(WAVEFORM_SINE);
  waveform2.begin(WAVEFORM_SQUARE);
  waveform3.begin(WAVEFORM_TRIANGLE);

  waveform1.amplitude(.1);
  waveform2.amplitude(.1);
  waveform3.amplitude(.1);

  waveform1.frequency(f1);
  waveform2.frequency(f2);
  waveform3.frequency(f3);

  waveform1.pulseWidth(.2);
  waveform2.pulseWidth(.33);
  waveform3.pulseWidth(.44);



  pinMode(ENC1,INPUT);
  pinMode(ENC2,INPUT);
 
  Serial.begin(9600);
  //while(!Serial); // wait for user to connect...
  //Serial.println("Basic NoInterrupts Test:");
  pinMode(led, OUTPUT);

   pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)  


  

}

int position  = 0;
int mainmode = 0;

//float _radi_pos = 0;
int curr_pos = 0;

elapsedMillis dbad;
uint8_t led_state=0;

int intensi = 22;


void loop() {


   


  long newPos = myEnc.read();
  
   MAINBTN.operateBUTTON();
   PLAYBTN.operateBUTTON();
  
  if (newPos != position) {
    position = newPos;
    Serial.println(position);
    digitalWrite(led,HIGH);
  }else{

    digitalWrite(led,LOW);
  }
   

   curr_pos = int(sin(position*.03)*6)+6;

   for(int i=0;i<12;i++){

      pixels.setPixelColor(  i,  pixels.Color(0,0,0)); 
   }
 
   

  if(mainmode==0){

      pixels.setPixelColor(  curr_pos,  pixels.Color(intensi,0,0)); 

      f1 = curr_pos*100;
  }

   if(mainmode==1){

      pixels.setPixelColor(  curr_pos,  pixels.Color(0,intensi,0)); 

      f2 = curr_pos*100;
  }


  if(mainmode==2){

      pixels.setPixelColor(  curr_pos,  pixels.Color(0,0,intensi)); 
      f3 = curr_pos*100;
  }

  pixels.show();


  if(PLAYBTN.is_pressed){

    intensi = 255;

  }else{

    intensi = 25;
  }

  if(MAINBTN.on_pressed){

      mainmode++;
      if(mainmode>2){
        mainmode=0;
      }

  }

  // Serial.println(int(position));

    waveform1.frequency(f1);
  waveform2.frequency(f2);
  waveform3.frequency(f3);


  // With any substantial delay added, Encoder can only track
  // very slow motion.  You may uncomment this line to see
  // how badly a delay affects your encoder.
    delay(2);
}
The most minimal Synthesizer to build walls of sound 🙂

Are we building a custom synthesizer, really?

Along with the Teensy Microcontroller comes a fantastic library that manages all the audio. It seems insane, but they offer a public online „wiring“ generator called Audio System Design Tool that produces all the code needed to get some sound out of the box 🙂

There is some tutorial videos for beginner out there – here is one to begin with to get some idea for the basics

What’s the input?

For controlling the synth we have a endless range of possibilities from simple button input, matricies, sensors or even generative algorithms. For this example, we will transform a used keyboard into an input device for the synth.

https://www.pjrc.com/teensy/td_libs_PS2Keyboard.html
https://www.youtube.com/watch?v=PmJlDlI-Pb4

tbc.