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.
USING PURE DATA TO SIMULATE THE SYNTH
PureData is one ancient, but still working, node based program we an use to simulate our audio setup instantly.
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); }
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.