Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 15 of 15

Thread: Anyone Doing Pitch Shifting?

  1. #1

    Anyone Doing Pitch Shifting?

    Hey Everyone,

    Just wondering if anyone is doing pitch shifting (i.e. voice changing/modulation) with Teensy 3.2 and Audio Shield?

    I have been doing some research on how to accomplish this and have experimented with some code (using the existing audio library objects) to see if I could accomplish it but so far have not had any success.

    Any help would be appreciated!

    Thanks!

  2. #2
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    69
    Yes, I used a delay object made by Byron Jacqout at Sparkfun. This object has a input for a modulation signal i.e the input determine the length of the delay. And then I feed that input with a triangle wave. Then the pitch will change up or down dependent of the direction and frequency of the triangle wave.
    But, you need to have two "signal paths" and change the phase of each triangle wave and mix the signals in the end...

    This article describe this:
    https://se.mathworks.com/help/audio/....mathworks.com

    The delay object can be found here:
    https://github.com/Jacquot-SFE/Synth...c-delay-sketch
    mod-delay.cpp and mod-delay.h is the files you will need.

    I have some code that is functional but far from optimal!!
    This is used with the openeffectsproject box and includes some other functions. You should look what is happening to waveform1 and 2 and you need to set up the delay buffers: delaybuf[LEN] ..

    Pot 1 is used to adjust the pitch down.
    Schematic can be found here:
    http://openeffectsproject.com/wp-con...hematic2_2.pdf



    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include <Adafruit_NeoPixel.h>
    #include <Adafruit_GFX.h>
    #include <Adafruit_SSD1306.h>
    #include <Bounce.h> 
    #include <EEPROM.h> 
    #include "mod-delay.h"
    using namespace std;
    
    #define OLED_RESET 4
    Adafruit_SSD1306 display(OLED_RESET);
    #define PIN            8  //changed from last rev //pin3 on rev 2, pin 8 on rev 2.1 ->
    #define NUMPIXELS      10 //changed from last rev
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
    
    
    // GUItool: begin automatically generated code
    AudioInputI2S            i2s2;           //xy=94,397
    AudioSynthWaveform       waveform1;      //xy=95,302
    AudioSynthWaveform       waveform2;      //xy=98,476
    AudioSynthWaveformDc     dc1;            //xy=100,341
    AudioAnalyzePeak         peak1;          //xy=251,372
    AudioMixer4              inmix;          //xy=251,424
    AudioMixer4              mixer2;         //xy=252,321
    AudioMixer4              mixer3;         //xy=252,495
    AudioSynthWaveformDc     dc2;            //xy=265,256
    AudioFilterBiquad        biquad2;        //xy=377,364
    AudioSynthWaveform       waveform4;      //xy=423,464
    AudioFilterStateVariable filter3;        //xy=427,249
    AudioEffectModDelay      xdly2;          //xy=501,399
    AudioEffectModDelay      xdly;           //xy=503,327
    AudioSynthWaveform       waveform3;      //xy=512,363
    AudioAnalyzeNoteFrequency notefreq1;      //xy=554,236
    AudioEffectMultiply      multiply1;      //xy=646,352
    AudioEffectMultiply      multiply2;      //xy=650,404
    AudioMixer4              outmix;         //xy=778,393
    AudioSynthWaveform       waveform5;      //xy=869,493
    AudioFilterBiquad        biquad1;        //xy=904,402
    AudioMixer4              mixer1;         //xy=1036,441
    AudioAnalyzePeak         peak2;          //xy=1037,537
    AudioSynthWaveformDc     dc3;            //xy=1038,497
    AudioEffectMultiply      multiply3;      //xy=1160,471
    AudioOutputI2S           i2s1;           //xy=1292,473
    AudioConnection          patchCord1(i2s2, 0, inmix, 0);
    AudioConnection          patchCord2(i2s2, 0, peak1, 0);
    AudioConnection          patchCord3(waveform1, 0, mixer2, 0);
    AudioConnection          patchCord4(waveform2, 0, mixer3, 0);
    AudioConnection          patchCord5(dc1, 0, mixer2, 1);
    AudioConnection          patchCord6(dc1, 0, mixer3, 1);
    AudioConnection          patchCord7(inmix, 0, mixer1, 1);
    AudioConnection          patchCord8(inmix, 0, filter3, 0);
    AudioConnection          patchCord9(inmix, biquad2);
    AudioConnection          patchCord10(mixer2, 0, xdly, 1);
    AudioConnection          patchCord11(mixer3, 0, xdly2, 1);
    AudioConnection          patchCord12(dc2, 0, filter3, 1);
    AudioConnection          patchCord13(biquad2, 0, xdly2, 0);
    AudioConnection          patchCord14(biquad2, 0, xdly, 0);
    AudioConnection          patchCord15(waveform4, 0, multiply2, 1);
    AudioConnection          patchCord16(filter3, 0, notefreq1, 0);
    AudioConnection          patchCord17(xdly2, 0, multiply2, 0);
    AudioConnection          patchCord18(xdly, 0, multiply1, 0);
    AudioConnection          patchCord19(waveform3, 0, multiply1, 1);
    AudioConnection          patchCord20(multiply1, 0, outmix, 0);
    AudioConnection          patchCord21(multiply2, 0, outmix, 1);
    AudioConnection          patchCord22(outmix, biquad1);
    AudioConnection          patchCord23(waveform5, peak2);
    AudioConnection          patchCord24(waveform5, 0, mixer1, 2);
    AudioConnection          patchCord25(biquad1, 0, mixer1, 0);
    AudioConnection          patchCord26(mixer1, 0, multiply3, 0);
    AudioConnection          patchCord27(dc3, 0, multiply3, 1);
    AudioConnection          patchCord28(multiply3, 0, i2s1, 0);
    AudioConnection          patchCord29(multiply3, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=579,550
    // GUItool: end automatically generated code
    
    
    // This is about max (97%!) for internal RAM
    static const uint32_t LEN = 0x01500; //1500ok...
    int16_t delaybuf[LEN];
    int16_t delaybuf2[LEN];
    
    uint32_t next;
    
    int16_t ArbWave[256] = {0,0,0,0,7327,10362,12691,14654,16384,17947,19385,20724,21981,23170,24301,25381,26418,27415,28377,29308,30210,31086,31937,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,31937,31086,30210,29308,28377,27415,26418,25381,24301,
    23170,21981,20724,19385,17947,16384,14654,12691,10362,7327,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,
    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,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};
    
    /***************/
    
    //Pinout board rev1
    int Pot1 = A6;    // select the input pin for the potentiometer
    int Pot2 = A3;    // select the input pin for the potentiometer
    int Pot3 = A2;    // select the input pin for the potentiometer
    int Pot4 = A1;    // select the input pin for the potentiometer
    int CV1 = A10;
    int CV2 = A11;
    
    int Tap1 = 2;
    int Tap2 = 3;
    
    int SW1 = A12;
    int SW2 = A13;
    
    //int ledPin2 = 4;      // select the pin for the LED
    //int ledPin3 = 5;      // select the pin for the LED
    int relayL = 4;      // select the pin for the RelayL
    int relayR = 5;      // select the pin for the RelayR
    
    //Variables for values
    boolean Tap1Value = 0;
    boolean Tap1ValueOld = 1; 
    boolean Tap2Value = 0;
    float Pot1Value = 0;  // variable to store the value coming from the sensor
    float Pot2Value = 0;  // variable to store the value coming from the sensor
    float Pot3Value = 0;  // variable to store the value coming from the sensor
    float Pot4Value = 0;  // variable to store the value coming from the sensor
    int CV1Value = 0;
    int CV2Value = 0;
    int SW1Value = 0;
    int SW2Value = 0;
    float Pot1ValueOld = 0;  // variable to store the previous value
    float Pot2ValueOld = 0;  
    float Pot3ValueOld = 0;  
    float Pot4ValueOld = 0;  
    int SW1ValueOld = 0;
    int SW2ValueOld = 0;
    int CV1ValueOld = 0;
    int CV2ValueOld = 0;
    float margin  = 0.01;
    int InLevel = 0;
    int OutLevel = 16;
    int Tap1Timer = 0;
    
    // Variables 
    int ledStateONOFF = LOW;         // the current state of the output pin
    int ledStateONOFF_old = LOW;
    int buttonState0;             // the current reading from the input pin
    int lastButtonState0 = LOW;   // the previous reading from the input pin
    int ledStateBOOST = LOW;         // the current state of the output pin
    int ledStateBOOST_old = LOW;
    int buttonState1;             // the current reading from the input pin
    int lastButtonState1 = LOW;   // the previous reading from the input pin
    
    unsigned long lastDebounceTime0 = 0;  // the last time the output pin was toggled
    unsigned long lastDebounceTime1 = 0;  // the last time the output pin was toggled
    unsigned long debounceDelay = 20;    // the debounce time; increase if the output flickers
    
    const int myInput = AUDIO_INPUT_LINEIN;
    
    int i,k;
    
    float s_freq = 3;
    float s_depth = 1;
    
    long sum, sum_old;
    long thresh = 0;
    float freq_per = 0;
    byte pd_state = 0;
    const float sample_freq = 44100;
    float Waveform_multi = 2.5;
    uint32_t off =pixels.Color(0,0,0);
    uint32_t blue =pixels.Color(0,0,50);
    uint32_t red =pixels.Color(50,0,0);
    uint32_t green =pixels.Color(0,40,0);
    uint32_t yellow =pixels.Color(40,40,0);
    uint32_t white =pixels.Color(30,30,30);
    
    float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
    {
      return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }
    Bounce OnOff = Bounce(Tap1, 30 ); 
    Bounce BoostOnOff = Bounce(Tap2, 30); 
    elapsedMillis ClippingInterval;  //timer that is used for peak reading
    elapsedMillis HoldValue;  //timer that is used for Volume Adjustment
    
    void setup() {
      // put your setup code here, to run once:
    
      Serial.begin(115200);
      delay(100);
      AudioMemory(100);
      pixels.begin();
      for (int i = 0;i<NUMPIXELS;i++){
        pixels.setPixelColor(i,off);
       } 
      pixels.show();
      float fc = 300;
      biquad1.setHighpass(0,5,0.7);
      biquad1.setHighpass(1,5,0.7);  
      biquad1.setLowpass(2,fc,0.7);  
      biquad1.setLowpass(3,fc,0.7);    
     // biquad2.setHighpass(0,5,0.7);
     // biquad2.setHighpass(1,5,0.7); 
      biquad2.setLowpass(0,fc,0.7);  
      biquad2.setLowpass(1,fc,0.7);   
      biquad2.setLowpass(2,fc,0.7);  
      biquad2.setLowpass(3,fc,0.7);  
      AudioNoInterrupts();
      float mod_freq = 4;//4
      waveform1.begin(1,mod_freq,WAVEFORM_ARBITRARY);
      waveform1.arbitraryWaveform(DelayWave,50);
      waveform2.phase(20);
      waveform2.begin(1,mod_freq,WAVEFORM_ARBITRARY);
      waveform2.arbitraryWaveform(DelayWave,50);  
      waveform2.phase(200);
      
      waveform3.begin(1,mod_freq,WAVEFORM_ARBITRARY);  
      waveform3.arbitraryWaveform(ArbWave,50);
      waveform3.phase(0);  
      waveform4.begin(1,mod_freq,WAVEFORM_ARBITRARY); 
      waveform4.arbitraryWaveform(ArbWave,50);
      waveform4.phase(180);  
      
      notefreq1.begin(.8);
      waveform5.begin(.5,20,WAVEFORM_TRIANGLE);
      inmix.gain(0, 2);
      //inmix.gain(1, 0.9);// set by knob
      //inmix.gain(2, 0.02);
    
      outmix.gain(0, 1.0);
      outmix.gain(1, 1.0);
      mixer1.gain(0,0.7);
      mixer1.gain(1,0.7);
      mixer1.gain(2,.3);
      mixer1.gain(3,0);
     
      mixer2.gain(0,1);
      mixer2.gain(1, 0);
      mixer3.gain(0,1);
      mixer3.gain(1, 0);
      
      filter3.frequency(300);
      filter3.resonance(.7);  
    
      xdly.setbuf(LEN, delaybuf);
      xdly2.setbuf(LEN, delaybuf2);
      
      //sgtl5000_1.enable();
      //sgtl5000_1.volume(0.5);
      sgtl5000_1.enable();  // Enable the audio shield
      //sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
      sgtl5000_1.unmuteHeadphone();
      sgtl5000_1.lineInLevel(EEPROM.read(0));   // stored input gain
      sgtl5000_1.lineOutLevel(17); // 2,53Vp-p
      sgtl5000_1.dacVolume (1);
      
      AudioInterrupts();
      pinMode(relayL, OUTPUT);
      pinMode(relayR, OUTPUT);
      pinMode(Tap1, INPUT);
      pinMode(Tap2, INPUT);
      sgtl5000_1.autoVolumeControl(0,0,0,-3.0,200,2000);  //maxGain,response,hard limit,threshold,attack, decay
      Serial.println("Setup complete.");
      dc1.amplitude(0);
      dc2.amplitude(0);
      dc3.amplitude(1);
      pixels.setPixelColor(0,red);
      pixels.show();
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      OnOff.update();      //update the bounce
      BoostOnOff.update();
    
      int reading0 = digitalRead(Tap1);
      int reading1 = digitalRead(Tap2);
      // read the value from the sensor:
      Pot1Value = analogRead(Pot1);    //Sub
      Pot2Value = analogRead(Pot2);    //Tone
      Pot3Value = analogRead(Pot3);    //Mix
      Pot4Value = analogRead(Pot4);    //Master
      Tap1Value = digitalRead(Tap1);   //On off
      Tap2Value = digitalRead(Tap2);   //Sub only
      SW1Value = analogRead(SW1);      //Tone setting
      SW2Value = analogRead(SW2);      //Octave up
      CV1Value = analogRead(CV1);      //EXP1 input
      CV2Value = analogRead(CV2);      //EXP2 input
    
      SW1Value = map(SW1Value, 0, 1023, 5, 0);            //Tone
      SW1Value = constrain(SW1Value, 1, 5);
      SW2Value = map(SW2Value, 0, 1023, 5, 0);              
      SW2Value = constrain(SW2Value, 1, 5);          //Octave up
      
    //********************ON -OFF handling**********************
      buttonState0 = OnOff.read();
      if (lastButtonState0 != buttonState0){   
          if (buttonState0 == LOW) {
            ledStateONOFF = !ledStateONOFF;
          }
      }
      buttonState1 = BoostOnOff.read();
      if (lastButtonState1 != buttonState1){ 
          if (buttonState1 == LOW) {
            ledStateBOOST = !ledStateBOOST;
          }
      } 
      // set the outputs:
    if (ledStateONOFF != ledStateONOFF_old){  
      pixels.setPixelColor(2, pixels.Color(ledStateONOFF*30,(ledStateONOFF*30),ledStateONOFF*30)); // ONOFF bright white color. 
      digitalWrite(relayL, ledStateONOFF); 
      digitalWrite(relayR, ledStateONOFF); 
      ledStateONOFF_old = ledStateONOFF; 
      pixels.show(); // This sends the updated pixel color to the hardware.
    }
    if (ledStateBOOST != ledStateBOOST_old){  
      pixels.setPixelColor(1, pixels.Color(ledStateBOOST*30,(ledStateBOOST*30),ledStateBOOST*30)); // OCTAVE ONLY bright white color. 
      ledStateBOOST_old = ledStateBOOST; 
      pixels.show(); // This sends the updated pixel color to the hardware.
    }
      lastButtonState0 = buttonState0;  //save readings to next round
      lastButtonState1 = buttonState1; 
    
    /////////////////////////////////////////////////////
    int margin = 12;
    
      //Change tone controller/shape    
    if (SW1Value != SW1ValueOld){
        if (SW1Value == 3)waveform5.begin(WAVEFORM_SQUARE);
        else if (SW1Value == 5)waveform5.begin(WAVEFORM_TRIANGLE);
        else waveform5.begin(WAVEFORM_SAWTOOTH);
        Serial.println(" New wave: ");
        Serial.println(SW1Value);
        SW1ValueOld = SW1Value;    
    }
    
      if (notefreq1.available()){
        if (SW2Value == 1){
          waveform5.frequency(notefreq1.read()* .5); //1 octave down
          waveform5.amplitude(peak1.read());
          }
        else if (SW2Value == 5){
            waveform5.frequency(notefreq1.read()* 2.5); //1.5 octave up
            waveform5.amplitude(peak1.read());
            }
        else {
            waveform5.frequency(notefreq1.read()* 2.); //1 octave up.
            waveform5.amplitude(peak1.read());
        } 
    }
      
    if(!ledStateONOFF){  //OFF
      //mixer1.gain(0,0);
      //mixer1.gain(1,1);
     // mixer1.gain(2, 0);
      //mixer1.gain(3, 0);  
      }
    else if(ledStateBOOST && ledStateONOFF){ //sub only
      mixer1.gain(0,1);
      mixer1.gain(1,0);
    //  mixer1.gain(2, 0);
      mixer1.gain(3, 0);  
      }
    else{              //On - mixed signals
        mixer1.gain(0,(float)Pot3Value/0x3ff);
        mixer1.gain(1,(1-(float)Pot3Value/0x3ff));
     //   mixer1.gain(2, 0);
        mixer1.gain(3, 0);
      }
    
    if ((Pot1Value <= (Pot1ValueOld-margin)) || (Pot1Value >= (Pot1ValueOld+margin)))  {
      float note =  (float)Pot1Value/0x3ff;
      if (note > 0.85)note = 0.93;
      else if ((note <= 0.85)&&(note > 0.5))note = 0.62;
      else if ((note <= 0.5)&&(note > 0.32))note = 0.37;
      else if ((note <= 0.32)&&(note > 0.0))note = 0.27;
      AudioNoInterrupts();
      waveform1.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      waveform2.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      AudioInterrupts();
      Pot1ValueOld = Pot1Value;
      Serial.print("Pot1:");
      Serial.println(note);
        }
    
    if ((CV1Value <= (CV1ValueOld-margin)) || (CV1Value >= (CV1ValueOld+margin)))  {
      float note =  mapfloat(CV1Value,0,190,0,1.0);
      if (note > 0.85)note = 0.93;
     // else if ((note <= 0.85)&&(note > 0.5))note = 0.62;
     // else if ((note <= 0.5)&&(note > 0.32))note = 0.37;
     // else if ((note <= 0.32)&&(note > 0.0))note = 0.27;
      else if (note <= 0.1)note = 0.1;
      //else note = 0.27;
      Serial.print("CV1:");
      Serial.println(note);
      waveform1.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      waveform2.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      CV1ValueOld = CV1Value;
        }    
       
    if ((Pot2Value <= (Pot2ValueOld-margin)) || (Pot2Value >= (Pot2ValueOld+margin)))  {  
      Pot2ValueOld = Pot2Value;
      if (Pot2Value > 990)Pot2Value = 1024; 
      mixer1.gain(2,1-(float)(Pot2Value/0x3ff));
      Serial.print("Pot2:");
      Serial.println(Pot2Value);  
    
        }
    if ((CV2Value <= (CV2ValueOld-margin)) || (CV2Value >= (CV2ValueOld+margin)))  {  
      float mixLevel = mapfloat(CV2Value,0,190,0,1.0);
      mixer1.gain(2,mixLevel);
      Serial.print("Cv2:");
      Serial.println(mixLevel);
      CV2ValueOld = CV2Value;
        }    
        
    if ((Pot3Value <= (Pot3ValueOld-margin)) || (Pot3Value >= (Pot3ValueOld+margin)))  {  
      mixer1.gain(0,(float)Pot3Value/0x3ff);
      mixer1.gain(1,(1-(float)Pot3Value/0x3ff));
      Serial.print("Pot3:");
      Serial.println(Pot3Value);
      Pot3ValueOld = Pot3Value;
        }
        
    if ((Pot4Value <= (Pot4ValueOld-margin)) || (Pot4Value >= (Pot4ValueOld+margin)))  {  
      int outlevel = map(Pot4Value,0,1023,13,31);
      sgtl5000_1.lineOutLevel(outlevel);
      Serial.print("Pot4:");
      Serial.println(Pot4Value);  
      Pot4ValueOld = Pot4Value;
        }   
         
    
    //*******************INPUT/OUTPUT LEVEL ADJUST************************
    
     if (!Tap1Value && (Tap1Value == Tap1ValueOld)){    //When Mode/tap is held down you can adjust the input gain of the input
       Tap1Timer++;
       delay(100);
      if (Tap1Timer > 15){
        InLevel = map(Pot4Value,0,1023,15,0);
        sgtl5000_1.lineInLevel(InLevel);
        pixels.setPixelColor(0, pixels.Color(InLevel*15,(InLevel*15),InLevel*0));
        pixels.show(); // This sends the updated pixel color to the hardware.   
        digitalWrite(relayL, 1); 
        digitalWrite(relayR, 1); 
        ledStateONOFF = 1;
        Pot4ValueOld = Pot4Value;
        }
      }
     else if (Tap1Timer > 15){    //only write to EEPROM once
        EEPROM.write(0, InLevel);
        Serial.println(InLevel);
        pixels.setPixelColor(0, red);
        pixels.show(); // This sends the updated pixel color to the hardware.  
        Tap1Timer = 0;
      }
     else if (Tap1Value){
       Tap1Timer = 0;
      }
    
    
    /*
    if (SW1Value == 1){    //HP pos for Output level
     if (!Tap1Value && (Tap1Value == Tap1ValueOld)){    //When Mode/tap is held down you can adjust the input gain of the input
     // Serial.print("TIME");
       Tap1Timer++;
      if (Tap1Timer > 15){
        outLevel = analogRead(Pot1);
        outLevel = mapfloat(outLevel,0,1023,10,1);
        delay(100);
        //analogWrite(ledPin2, 255);  
        //display.clearDisplay();   
        display.setTextSize(1);
        display.setTextColor(WHITE);
        display.setCursor(5, 10);
        display.print("Output:");
        display.print(outLevel); 
        display.display();
        }
      }
      else if (Tap1Timer > 15){    //only write to EEPROM once
        EEPROM.write(1, outLevel);
        Tap1Timer = 0;
      }
      else if (Tap1Value){
      //  peak = peak1.read();   //read the signal output peak
      if (peak == -1)(peak = 0);
      //analogWrite(ledPin2, peak*100);
      }
    }
    */
       /*
      
       Serial.print(" Pot1: ");
       Serial.print(Pot1Value);   
       Serial.print(" Pot2: ");
       Serial.print(Pot2Value);
       Serial.print(" Pot3: ");
       Serial.print(Pot3Value);
       Serial.print(" Pot4: ");
       Serial.print(Pot4Value);
       Serial.print(" SW1: ");
       Serial.print(SW1Value);
       Serial.print(" SW2: ");
       Serial.print(SW2Value);
      
       Serial.print(" CV1: ");
       Serial.print(CV1Value);
       Serial.print(" CV2: ");
       Serial.println(CV2Value);
     */
     
    
       SW2ValueOld = SW2Value; 
       Tap1ValueOld = Tap1Value;
    
       if (ClippingInterval > 1000){
        if (ledStateONOFF){
            float peakLevel = peak1.readPeakToPeak();
            if (peakLevel > 1) pixels.setPixelColor(2,red); //indicate high input level
            else pixels.setPixelColor(2,white);
            pixels.show(); // This sends the updated pixel color to the hardware.
            ClippingInterval = 0;
        }
       }
       
    /*
        Serial.print("Diagnostics: ");
        Serial.print(" max, buffs: ");
        Serial.print(AudioProcessorUsageMax());
        Serial.print(" ");
        Serial.println(AudioMemoryUsageMax());
        AudioProcessorUsageMaxReset();
        xdly.inspect();
        */
    }

  3. #3
    Quote Originally Posted by omjanger View Post
    Yes, I used a delay object made by Byron Jacqout at Sparkfun. This object has a input for a modulation signal i.e the input determine the length of the delay. And then I feed that input with a triangle wave. Then the pitch will change up or down dependent of the direction and frequency of the triangle wave.
    But, you need to have two "signal paths" and change the phase of each triangle wave and mix the signals in the end...

    This article describe this:
    https://se.mathworks.com/help/audio/....mathworks.com

    The delay object can be found here:
    https://github.com/Jacquot-SFE/Synth...c-delay-sketch
    mod-delay.cpp and mod-delay.h is the files you will need.

    I have some code that is functional but far from optimal!!
    This is used with the openeffectsproject box and includes some other functions. You should look what is happening to waveform1 and 2 and you need to set up the delay buffers: delaybuf[LEN] ..

    Pot 1 is used to adjust the pitch down.
    Schematic can be found here:
    http://openeffectsproject.com/wp-con...hematic2_2.pdf



    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include <Adafruit_NeoPixel.h>
    #include <Adafruit_GFX.h>
    #include <Adafruit_SSD1306.h>
    #include <Bounce.h> 
    #include <EEPROM.h> 
    #include "mod-delay.h"
    using namespace std;
    
    #define OLED_RESET 4
    Adafruit_SSD1306 display(OLED_RESET);
    #define PIN            8  //changed from last rev //pin3 on rev 2, pin 8 on rev 2.1 ->
    #define NUMPIXELS      10 //changed from last rev
    Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
    
    
    // GUItool: begin automatically generated code
    AudioInputI2S            i2s2;           //xy=94,397
    AudioSynthWaveform       waveform1;      //xy=95,302
    AudioSynthWaveform       waveform2;      //xy=98,476
    AudioSynthWaveformDc     dc1;            //xy=100,341
    AudioAnalyzePeak         peak1;          //xy=251,372
    AudioMixer4              inmix;          //xy=251,424
    AudioMixer4              mixer2;         //xy=252,321
    AudioMixer4              mixer3;         //xy=252,495
    AudioSynthWaveformDc     dc2;            //xy=265,256
    AudioFilterBiquad        biquad2;        //xy=377,364
    AudioSynthWaveform       waveform4;      //xy=423,464
    AudioFilterStateVariable filter3;        //xy=427,249
    AudioEffectModDelay      xdly2;          //xy=501,399
    AudioEffectModDelay      xdly;           //xy=503,327
    AudioSynthWaveform       waveform3;      //xy=512,363
    AudioAnalyzeNoteFrequency notefreq1;      //xy=554,236
    AudioEffectMultiply      multiply1;      //xy=646,352
    AudioEffectMultiply      multiply2;      //xy=650,404
    AudioMixer4              outmix;         //xy=778,393
    AudioSynthWaveform       waveform5;      //xy=869,493
    AudioFilterBiquad        biquad1;        //xy=904,402
    AudioMixer4              mixer1;         //xy=1036,441
    AudioAnalyzePeak         peak2;          //xy=1037,537
    AudioSynthWaveformDc     dc3;            //xy=1038,497
    AudioEffectMultiply      multiply3;      //xy=1160,471
    AudioOutputI2S           i2s1;           //xy=1292,473
    AudioConnection          patchCord1(i2s2, 0, inmix, 0);
    AudioConnection          patchCord2(i2s2, 0, peak1, 0);
    AudioConnection          patchCord3(waveform1, 0, mixer2, 0);
    AudioConnection          patchCord4(waveform2, 0, mixer3, 0);
    AudioConnection          patchCord5(dc1, 0, mixer2, 1);
    AudioConnection          patchCord6(dc1, 0, mixer3, 1);
    AudioConnection          patchCord7(inmix, 0, mixer1, 1);
    AudioConnection          patchCord8(inmix, 0, filter3, 0);
    AudioConnection          patchCord9(inmix, biquad2);
    AudioConnection          patchCord10(mixer2, 0, xdly, 1);
    AudioConnection          patchCord11(mixer3, 0, xdly2, 1);
    AudioConnection          patchCord12(dc2, 0, filter3, 1);
    AudioConnection          patchCord13(biquad2, 0, xdly2, 0);
    AudioConnection          patchCord14(biquad2, 0, xdly, 0);
    AudioConnection          patchCord15(waveform4, 0, multiply2, 1);
    AudioConnection          patchCord16(filter3, 0, notefreq1, 0);
    AudioConnection          patchCord17(xdly2, 0, multiply2, 0);
    AudioConnection          patchCord18(xdly, 0, multiply1, 0);
    AudioConnection          patchCord19(waveform3, 0, multiply1, 1);
    AudioConnection          patchCord20(multiply1, 0, outmix, 0);
    AudioConnection          patchCord21(multiply2, 0, outmix, 1);
    AudioConnection          patchCord22(outmix, biquad1);
    AudioConnection          patchCord23(waveform5, peak2);
    AudioConnection          patchCord24(waveform5, 0, mixer1, 2);
    AudioConnection          patchCord25(biquad1, 0, mixer1, 0);
    AudioConnection          patchCord26(mixer1, 0, multiply3, 0);
    AudioConnection          patchCord27(dc3, 0, multiply3, 1);
    AudioConnection          patchCord28(multiply3, 0, i2s1, 0);
    AudioConnection          patchCord29(multiply3, 0, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=579,550
    // GUItool: end automatically generated code
    
    
    // This is about max (97%!) for internal RAM
    static const uint32_t LEN = 0x01500; //1500ok...
    int16_t delaybuf[LEN];
    int16_t delaybuf2[LEN];
    
    uint32_t next;
    
    int16_t ArbWave[256] = {0,0,0,0,7327,10362,12691,14654,16384,17947,19385,20724,21981,23170,24301,25381,26418,27415,28377,29308,30210,31086,31937,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
    32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,31937,31086,30210,29308,28377,27415,26418,25381,24301,
    23170,21981,20724,19385,17947,16384,14654,12691,10362,7327,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,
    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,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};
    
    /***************/
    
    //Pinout board rev1
    int Pot1 = A6;    // select the input pin for the potentiometer
    int Pot2 = A3;    // select the input pin for the potentiometer
    int Pot3 = A2;    // select the input pin for the potentiometer
    int Pot4 = A1;    // select the input pin for the potentiometer
    int CV1 = A10;
    int CV2 = A11;
    
    int Tap1 = 2;
    int Tap2 = 3;
    
    int SW1 = A12;
    int SW2 = A13;
    
    //int ledPin2 = 4;      // select the pin for the LED
    //int ledPin3 = 5;      // select the pin for the LED
    int relayL = 4;      // select the pin for the RelayL
    int relayR = 5;      // select the pin for the RelayR
    
    //Variables for values
    boolean Tap1Value = 0;
    boolean Tap1ValueOld = 1; 
    boolean Tap2Value = 0;
    float Pot1Value = 0;  // variable to store the value coming from the sensor
    float Pot2Value = 0;  // variable to store the value coming from the sensor
    float Pot3Value = 0;  // variable to store the value coming from the sensor
    float Pot4Value = 0;  // variable to store the value coming from the sensor
    int CV1Value = 0;
    int CV2Value = 0;
    int SW1Value = 0;
    int SW2Value = 0;
    float Pot1ValueOld = 0;  // variable to store the previous value
    float Pot2ValueOld = 0;  
    float Pot3ValueOld = 0;  
    float Pot4ValueOld = 0;  
    int SW1ValueOld = 0;
    int SW2ValueOld = 0;
    int CV1ValueOld = 0;
    int CV2ValueOld = 0;
    float margin  = 0.01;
    int InLevel = 0;
    int OutLevel = 16;
    int Tap1Timer = 0;
    
    // Variables 
    int ledStateONOFF = LOW;         // the current state of the output pin
    int ledStateONOFF_old = LOW;
    int buttonState0;             // the current reading from the input pin
    int lastButtonState0 = LOW;   // the previous reading from the input pin
    int ledStateBOOST = LOW;         // the current state of the output pin
    int ledStateBOOST_old = LOW;
    int buttonState1;             // the current reading from the input pin
    int lastButtonState1 = LOW;   // the previous reading from the input pin
    
    unsigned long lastDebounceTime0 = 0;  // the last time the output pin was toggled
    unsigned long lastDebounceTime1 = 0;  // the last time the output pin was toggled
    unsigned long debounceDelay = 20;    // the debounce time; increase if the output flickers
    
    const int myInput = AUDIO_INPUT_LINEIN;
    
    int i,k;
    
    float s_freq = 3;
    float s_depth = 1;
    
    long sum, sum_old;
    long thresh = 0;
    float freq_per = 0;
    byte pd_state = 0;
    const float sample_freq = 44100;
    float Waveform_multi = 2.5;
    uint32_t off =pixels.Color(0,0,0);
    uint32_t blue =pixels.Color(0,0,50);
    uint32_t red =pixels.Color(50,0,0);
    uint32_t green =pixels.Color(0,40,0);
    uint32_t yellow =pixels.Color(40,40,0);
    uint32_t white =pixels.Color(30,30,30);
    
    float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
    {
      return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    }
    Bounce OnOff = Bounce(Tap1, 30 ); 
    Bounce BoostOnOff = Bounce(Tap2, 30); 
    elapsedMillis ClippingInterval;  //timer that is used for peak reading
    elapsedMillis HoldValue;  //timer that is used for Volume Adjustment
    
    void setup() {
      // put your setup code here, to run once:
    
      Serial.begin(115200);
      delay(100);
      AudioMemory(100);
      pixels.begin();
      for (int i = 0;i<NUMPIXELS;i++){
        pixels.setPixelColor(i,off);
       } 
      pixels.show();
      float fc = 300;
      biquad1.setHighpass(0,5,0.7);
      biquad1.setHighpass(1,5,0.7);  
      biquad1.setLowpass(2,fc,0.7);  
      biquad1.setLowpass(3,fc,0.7);    
     // biquad2.setHighpass(0,5,0.7);
     // biquad2.setHighpass(1,5,0.7); 
      biquad2.setLowpass(0,fc,0.7);  
      biquad2.setLowpass(1,fc,0.7);   
      biquad2.setLowpass(2,fc,0.7);  
      biquad2.setLowpass(3,fc,0.7);  
      AudioNoInterrupts();
      float mod_freq = 4;//4
      waveform1.begin(1,mod_freq,WAVEFORM_ARBITRARY);
      waveform1.arbitraryWaveform(DelayWave,50);
      waveform2.phase(20);
      waveform2.begin(1,mod_freq,WAVEFORM_ARBITRARY);
      waveform2.arbitraryWaveform(DelayWave,50);  
      waveform2.phase(200);
      
      waveform3.begin(1,mod_freq,WAVEFORM_ARBITRARY);  
      waveform3.arbitraryWaveform(ArbWave,50);
      waveform3.phase(0);  
      waveform4.begin(1,mod_freq,WAVEFORM_ARBITRARY); 
      waveform4.arbitraryWaveform(ArbWave,50);
      waveform4.phase(180);  
      
      notefreq1.begin(.8);
      waveform5.begin(.5,20,WAVEFORM_TRIANGLE);
      inmix.gain(0, 2);
      //inmix.gain(1, 0.9);// set by knob
      //inmix.gain(2, 0.02);
    
      outmix.gain(0, 1.0);
      outmix.gain(1, 1.0);
      mixer1.gain(0,0.7);
      mixer1.gain(1,0.7);
      mixer1.gain(2,.3);
      mixer1.gain(3,0);
     
      mixer2.gain(0,1);
      mixer2.gain(1, 0);
      mixer3.gain(0,1);
      mixer3.gain(1, 0);
      
      filter3.frequency(300);
      filter3.resonance(.7);  
    
      xdly.setbuf(LEN, delaybuf);
      xdly2.setbuf(LEN, delaybuf2);
      
      //sgtl5000_1.enable();
      //sgtl5000_1.volume(0.5);
      sgtl5000_1.enable();  // Enable the audio shield
      //sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.8);
      sgtl5000_1.unmuteHeadphone();
      sgtl5000_1.lineInLevel(EEPROM.read(0));   // stored input gain
      sgtl5000_1.lineOutLevel(17); // 2,53Vp-p
      sgtl5000_1.dacVolume (1);
      
      AudioInterrupts();
      pinMode(relayL, OUTPUT);
      pinMode(relayR, OUTPUT);
      pinMode(Tap1, INPUT);
      pinMode(Tap2, INPUT);
      sgtl5000_1.autoVolumeControl(0,0,0,-3.0,200,2000);  //maxGain,response,hard limit,threshold,attack, decay
      Serial.println("Setup complete.");
      dc1.amplitude(0);
      dc2.amplitude(0);
      dc3.amplitude(1);
      pixels.setPixelColor(0,red);
      pixels.show();
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
      OnOff.update();      //update the bounce
      BoostOnOff.update();
    
      int reading0 = digitalRead(Tap1);
      int reading1 = digitalRead(Tap2);
      // read the value from the sensor:
      Pot1Value = analogRead(Pot1);    //Sub
      Pot2Value = analogRead(Pot2);    //Tone
      Pot3Value = analogRead(Pot3);    //Mix
      Pot4Value = analogRead(Pot4);    //Master
      Tap1Value = digitalRead(Tap1);   //On off
      Tap2Value = digitalRead(Tap2);   //Sub only
      SW1Value = analogRead(SW1);      //Tone setting
      SW2Value = analogRead(SW2);      //Octave up
      CV1Value = analogRead(CV1);      //EXP1 input
      CV2Value = analogRead(CV2);      //EXP2 input
    
      SW1Value = map(SW1Value, 0, 1023, 5, 0);            //Tone
      SW1Value = constrain(SW1Value, 1, 5);
      SW2Value = map(SW2Value, 0, 1023, 5, 0);              
      SW2Value = constrain(SW2Value, 1, 5);          //Octave up
      
    //********************ON -OFF handling**********************
      buttonState0 = OnOff.read();
      if (lastButtonState0 != buttonState0){   
          if (buttonState0 == LOW) {
            ledStateONOFF = !ledStateONOFF;
          }
      }
      buttonState1 = BoostOnOff.read();
      if (lastButtonState1 != buttonState1){ 
          if (buttonState1 == LOW) {
            ledStateBOOST = !ledStateBOOST;
          }
      } 
      // set the outputs:
    if (ledStateONOFF != ledStateONOFF_old){  
      pixels.setPixelColor(2, pixels.Color(ledStateONOFF*30,(ledStateONOFF*30),ledStateONOFF*30)); // ONOFF bright white color. 
      digitalWrite(relayL, ledStateONOFF); 
      digitalWrite(relayR, ledStateONOFF); 
      ledStateONOFF_old = ledStateONOFF; 
      pixels.show(); // This sends the updated pixel color to the hardware.
    }
    if (ledStateBOOST != ledStateBOOST_old){  
      pixels.setPixelColor(1, pixels.Color(ledStateBOOST*30,(ledStateBOOST*30),ledStateBOOST*30)); // OCTAVE ONLY bright white color. 
      ledStateBOOST_old = ledStateBOOST; 
      pixels.show(); // This sends the updated pixel color to the hardware.
    }
      lastButtonState0 = buttonState0;  //save readings to next round
      lastButtonState1 = buttonState1; 
    
    /////////////////////////////////////////////////////
    int margin = 12;
    
      //Change tone controller/shape    
    if (SW1Value != SW1ValueOld){
        if (SW1Value == 3)waveform5.begin(WAVEFORM_SQUARE);
        else if (SW1Value == 5)waveform5.begin(WAVEFORM_TRIANGLE);
        else waveform5.begin(WAVEFORM_SAWTOOTH);
        Serial.println(" New wave: ");
        Serial.println(SW1Value);
        SW1ValueOld = SW1Value;    
    }
    
      if (notefreq1.available()){
        if (SW2Value == 1){
          waveform5.frequency(notefreq1.read()* .5); //1 octave down
          waveform5.amplitude(peak1.read());
          }
        else if (SW2Value == 5){
            waveform5.frequency(notefreq1.read()* 2.5); //1.5 octave up
            waveform5.amplitude(peak1.read());
            }
        else {
            waveform5.frequency(notefreq1.read()* 2.); //1 octave up.
            waveform5.amplitude(peak1.read());
        } 
    }
      
    if(!ledStateONOFF){  //OFF
      //mixer1.gain(0,0);
      //mixer1.gain(1,1);
     // mixer1.gain(2, 0);
      //mixer1.gain(3, 0);  
      }
    else if(ledStateBOOST && ledStateONOFF){ //sub only
      mixer1.gain(0,1);
      mixer1.gain(1,0);
    //  mixer1.gain(2, 0);
      mixer1.gain(3, 0);  
      }
    else{              //On - mixed signals
        mixer1.gain(0,(float)Pot3Value/0x3ff);
        mixer1.gain(1,(1-(float)Pot3Value/0x3ff));
     //   mixer1.gain(2, 0);
        mixer1.gain(3, 0);
      }
    
    if ((Pot1Value <= (Pot1ValueOld-margin)) || (Pot1Value >= (Pot1ValueOld+margin)))  {
      float note =  (float)Pot1Value/0x3ff;
      if (note > 0.85)note = 0.93;
      else if ((note <= 0.85)&&(note > 0.5))note = 0.62;
      else if ((note <= 0.5)&&(note > 0.32))note = 0.37;
      else if ((note <= 0.32)&&(note > 0.0))note = 0.27;
      AudioNoInterrupts();
      waveform1.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      waveform2.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      AudioInterrupts();
      Pot1ValueOld = Pot1Value;
      Serial.print("Pot1:");
      Serial.println(note);
        }
    
    if ((CV1Value <= (CV1ValueOld-margin)) || (CV1Value >= (CV1ValueOld+margin)))  {
      float note =  mapfloat(CV1Value,0,190,0,1.0);
      if (note > 0.85)note = 0.93;
     // else if ((note <= 0.85)&&(note > 0.5))note = 0.62;
     // else if ((note <= 0.5)&&(note > 0.32))note = 0.37;
     // else if ((note <= 0.32)&&(note > 0.0))note = 0.27;
      else if (note <= 0.1)note = 0.1;
      //else note = 0.27;
      Serial.print("CV1:");
      Serial.println(note);
      waveform1.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      waveform2.amplitude(note);//working values: 0.93 / 0.62 / 0.37 / 0.27
      CV1ValueOld = CV1Value;
        }    
       
    if ((Pot2Value <= (Pot2ValueOld-margin)) || (Pot2Value >= (Pot2ValueOld+margin)))  {  
      Pot2ValueOld = Pot2Value;
      if (Pot2Value > 990)Pot2Value = 1024; 
      mixer1.gain(2,1-(float)(Pot2Value/0x3ff));
      Serial.print("Pot2:");
      Serial.println(Pot2Value);  
    
        }
    if ((CV2Value <= (CV2ValueOld-margin)) || (CV2Value >= (CV2ValueOld+margin)))  {  
      float mixLevel = mapfloat(CV2Value,0,190,0,1.0);
      mixer1.gain(2,mixLevel);
      Serial.print("Cv2:");
      Serial.println(mixLevel);
      CV2ValueOld = CV2Value;
        }    
        
    if ((Pot3Value <= (Pot3ValueOld-margin)) || (Pot3Value >= (Pot3ValueOld+margin)))  {  
      mixer1.gain(0,(float)Pot3Value/0x3ff);
      mixer1.gain(1,(1-(float)Pot3Value/0x3ff));
      Serial.print("Pot3:");
      Serial.println(Pot3Value);
      Pot3ValueOld = Pot3Value;
        }
        
    if ((Pot4Value <= (Pot4ValueOld-margin)) || (Pot4Value >= (Pot4ValueOld+margin)))  {  
      int outlevel = map(Pot4Value,0,1023,13,31);
      sgtl5000_1.lineOutLevel(outlevel);
      Serial.print("Pot4:");
      Serial.println(Pot4Value);  
      Pot4ValueOld = Pot4Value;
        }   
         
    
    //*******************INPUT/OUTPUT LEVEL ADJUST************************
    
     if (!Tap1Value && (Tap1Value == Tap1ValueOld)){    //When Mode/tap is held down you can adjust the input gain of the input
       Tap1Timer++;
       delay(100);
      if (Tap1Timer > 15){
        InLevel = map(Pot4Value,0,1023,15,0);
        sgtl5000_1.lineInLevel(InLevel);
        pixels.setPixelColor(0, pixels.Color(InLevel*15,(InLevel*15),InLevel*0));
        pixels.show(); // This sends the updated pixel color to the hardware.   
        digitalWrite(relayL, 1); 
        digitalWrite(relayR, 1); 
        ledStateONOFF = 1;
        Pot4ValueOld = Pot4Value;
        }
      }
     else if (Tap1Timer > 15){    //only write to EEPROM once
        EEPROM.write(0, InLevel);
        Serial.println(InLevel);
        pixels.setPixelColor(0, red);
        pixels.show(); // This sends the updated pixel color to the hardware.  
        Tap1Timer = 0;
      }
     else if (Tap1Value){
       Tap1Timer = 0;
      }
    
    
    /*
    if (SW1Value == 1){    //HP pos for Output level
     if (!Tap1Value && (Tap1Value == Tap1ValueOld)){    //When Mode/tap is held down you can adjust the input gain of the input
     // Serial.print("TIME");
       Tap1Timer++;
      if (Tap1Timer > 15){
        outLevel = analogRead(Pot1);
        outLevel = mapfloat(outLevel,0,1023,10,1);
        delay(100);
        //analogWrite(ledPin2, 255);  
        //display.clearDisplay();   
        display.setTextSize(1);
        display.setTextColor(WHITE);
        display.setCursor(5, 10);
        display.print("Output:");
        display.print(outLevel); 
        display.display();
        }
      }
      else if (Tap1Timer > 15){    //only write to EEPROM once
        EEPROM.write(1, outLevel);
        Tap1Timer = 0;
      }
      else if (Tap1Value){
      //  peak = peak1.read();   //read the signal output peak
      if (peak == -1)(peak = 0);
      //analogWrite(ledPin2, peak*100);
      }
    }
    */
       /*
      
       Serial.print(" Pot1: ");
       Serial.print(Pot1Value);   
       Serial.print(" Pot2: ");
       Serial.print(Pot2Value);
       Serial.print(" Pot3: ");
       Serial.print(Pot3Value);
       Serial.print(" Pot4: ");
       Serial.print(Pot4Value);
       Serial.print(" SW1: ");
       Serial.print(SW1Value);
       Serial.print(" SW2: ");
       Serial.print(SW2Value);
      
       Serial.print(" CV1: ");
       Serial.print(CV1Value);
       Serial.print(" CV2: ");
       Serial.println(CV2Value);
     */
     
    
       SW2ValueOld = SW2Value; 
       Tap1ValueOld = Tap1Value;
    
       if (ClippingInterval > 1000){
        if (ledStateONOFF){
            float peakLevel = peak1.readPeakToPeak();
            if (peakLevel > 1) pixels.setPixelColor(2,red); //indicate high input level
            else pixels.setPixelColor(2,white);
            pixels.show(); // This sends the updated pixel color to the hardware.
            ClippingInterval = 0;
        }
       }
       
    /*
        Serial.print("Diagnostics: ");
        Serial.print(" max, buffs: ");
        Serial.print(AudioProcessorUsageMax());
        Serial.print(" ");
        Serial.println(AudioMemoryUsageMax());
        AudioProcessorUsageMaxReset();
        xdly.inspect();
        */
    }
    Thank you so much! I will definitely try this tonight!

  4. #4
    @omjanger Getting a compiler error that 'DelayWave' is not declared. I do not see it anywhere in the example you provided. Is this something you declare in your code somewhere?

    Code:
    'DelayWave' was not declared in this scope
    Thank you!

  5. #5
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    69
    Quote Originally Posted by lerxstrulz View Post
    @omjanger Getting a compiler error that 'DelayWave' is not declared. I do not see it anywhere in the example you provided. Is this something you declare in your code somewhere?

    Code:
    'DelayWave' was not declared in this scope
    Thank you!
    Yes... I took out some code to clean it up a little, but I removed too much=)

    Put this array into the code after the ArbWave:
    Code:
    int16_t DelayWave[256] = {500,500,500,500,572,715,858,1001,1144,1287,1430,1573,1716,1859,2002,2145,2288,2431,2574,2717,2860,3003,3146,3289,3432,3575,3718,3861,4004,4147,
    4290,4433,4576,4719,4862,5005,5148,5291,5434,5577,5720,5863,6006,6149,6292,6435,6578,6721,6864,7007,7150,7293,7436,7579,7722,7865,8008,8151,8294,
    8437,8580,8723,8866,9009,9152,9295,9438,9581,9724,9867,10010,10153,10296,10439,10582,10725,10868,11011,11154,11297,11440,11583,11726,11869,12012,
    12155,12298,12441,12584,12727,12870,13013,13156,13299,13442,13585,13728,13871,14014,14157,14300,14443,14586,14729,14872,15015,15158,15301,15444,
    15587,15730,15873,16016,16159,16302,16445,16588,16731,16874,17017,17160,17303,17446,17589,17732,17875,18018,18161,18304,18447,18590,18733,18876,
    19019,19162,19305,19448,19591,19734,19877,20020,20163,20306,20449,20592,20735,20878,21021,21164,21307,21450,21593,21736,21879,22022,22165,22308,
    22451,22594,22737,22880,23023,23166,23309,23452,23595,23738,23881,24024,24167,24310,24453,24596,24739,24882,25025,25168,25311,25454,25597,25740,
    25883,26026,26169,26312,26455,26598,26741,26884,27027,27170,27313,27456,27599,27742,27885,28028,28171,28314,28457,28600,28743,28886,29029,29172,
    29315,29458,29601,29744,29887,30030,30173,30316,30459,30602,30745,30888,31031,31174,31317,31440,31443,31443,31443,31443,31443,31443,31443,31200,
    31000,30700,30139,28835,27530,26226,24922,23617,22313,21009,19705,18401,17096,15792,14488,13184,11880,10575,9271,7967,6663,5359,4054,2750,1446,
    500,500};

  6. #6
    Quote Originally Posted by omjanger View Post
    Yes... I took out some code to clean it up a little, but I removed too much=)

    Put this array into the code after the ArbWave:
    Code:
    int16_t DelayWave[256] = {500,500,500,500,572,715,858,1001,1144,1287,1430,1573,1716,1859,2002,2145,2288,2431,2574,2717,2860,3003,3146,3289,3432,3575,3718,3861,4004,4147,
    4290,4433,4576,4719,4862,5005,5148,5291,5434,5577,5720,5863,6006,6149,6292,6435,6578,6721,6864,7007,7150,7293,7436,7579,7722,7865,8008,8151,8294,
    8437,8580,8723,8866,9009,9152,9295,9438,9581,9724,9867,10010,10153,10296,10439,10582,10725,10868,11011,11154,11297,11440,11583,11726,11869,12012,
    12155,12298,12441,12584,12727,12870,13013,13156,13299,13442,13585,13728,13871,14014,14157,14300,14443,14586,14729,14872,15015,15158,15301,15444,
    15587,15730,15873,16016,16159,16302,16445,16588,16731,16874,17017,17160,17303,17446,17589,17732,17875,18018,18161,18304,18447,18590,18733,18876,
    19019,19162,19305,19448,19591,19734,19877,20020,20163,20306,20449,20592,20735,20878,21021,21164,21307,21450,21593,21736,21879,22022,22165,22308,
    22451,22594,22737,22880,23023,23166,23309,23452,23595,23738,23881,24024,24167,24310,24453,24596,24739,24882,25025,25168,25311,25454,25597,25740,
    25883,26026,26169,26312,26455,26598,26741,26884,27027,27170,27313,27456,27599,27742,27885,28028,28171,28314,28457,28600,28743,28886,29029,29172,
    29315,29458,29601,29744,29887,30030,30173,30316,30459,30602,30745,30888,31031,31174,31317,31440,31443,31443,31443,31443,31443,31443,31443,31200,
    31000,30700,30139,28835,27530,26226,24922,23617,22313,21009,19705,18401,17096,15792,14488,13184,11880,10575,9271,7967,6663,5359,4054,2750,1446,
    500,500};
    Great thank you...got it working. Also checked out the video on the link you provided.

    I'm wanting to use this with a voice input and change the pitch of the voice up or down. It's compiled and running, but the voice is full of static and the waveforms are creating the random sounds I think this is a good start, though, so I will see what I can do with it and see if it will accomplish what I'm after. Thank you so much for the help!

  7. #7
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    69
    Good to hear! Would love to see what you can get from this.
    And yes, you should disable the "nintendo" sounds=) And you might want to mix the pitched voice with a bit of the original to get the right attack. Do you get some ticking artifacts in the sound from the delay line? This comes from when the delay line gets to the endpoints, and I didn't manage to get rid of all the artifacts in the sound.
    I have rounded the endpoints of the delayline modulation curve so that it never goes to zero and not change very fast. That helped. Maybe a solution would be to add a third delayline to ensure that the delay is always near the middle of the sawtooth wave and not even close to the top or bottom?

  8. #8
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    69
    Quote Originally Posted by lerxstrulz View Post
    Great thank you...got it working. Also checked out the video on the link you provided.

    I'm wanting to use this with a voice input and change the pitch of the voice up or down. It's compiled and running, but the voice is full of static and the waveforms are creating the random sounds I think this is a good start, though, so I will see what I can do with it and see if it will accomplish what I'm after. Thank you so much for the help!
    Did you manage to get it working?

  9. #9
    Senior Member
    Join Date
    Feb 2017
    Posts
    275
    So, it’s been awhile, but I finally got a chance to try out the technique referenced in Post #2. The Audio System connections look like this:

    Click image for larger version. 

Name:	Audio Object Connections.jpg 
Views:	75 
Size:	37.6 KB 
ID:	15626

    The blocks labeled “modulatedDelay1” and “modulatedDelay2” perform both the variable delay and amplitude modulation functions required by this technique.

    The input signal source is an I2S Microphone. The output drives a 3W I2S Amplifier and speaker.

    Here’s the electrical connection diagram:

    Click image for larger version. 

Name:	Electrical Connections.jpg 
Views:	38 
Size:	39.4 KB 
ID:	15627

    The rotary encoders adjust the amount of pitch shift (up to +/- 1 octave in the current implementation) and the ratio of unshifted to shifted input that is applied to the output.

    The full code is at my GitHub: ToneShift. This includes the code for the modulatedDelay Audio Library class. It’s based on the variable delay class linked in Post #2 above.

    The code for the rotary encoder library I used is also on my GitHub: NewEncoder. I wrote this because the standard encoder library has issues with the encoder that I used. First, because it’s a full-quadrature encoder, the standard library double counts on every step between detents. This encoder also has significant mechanical contact bounce that comes through using the standard library. My library address both of these issues as well as adds a few features.

    Bottom line with this Pitch Shift technique is that it produces a Really Cool voice changer -- especially if you combine it with some feedback or other audio effects.

    However, the overlap between the two delay lines (as one transitions off and the other transitions on) does produce artifacts in the signal. I think that’s unavoidable. You’re essentially trying to change the sampling rate while still playing the audio over the same time period. You’re either going to have to throw away or reuse samples. Those artifacts may bother an audio purist. Fortunately, I’m not one.

  10. #10
    Quote Originally Posted by omjanger View Post
    Did you manage to get it working?
    I got it working, but was still having some tonal quality issues and then had to put it down for a while.

    In the meantime, the Audio lib has been updated with a pitch shifter that works pretty well. It does have some artifacting depending upon settings.

  11. #11
    Junior Member JotaEfe13's Avatar
    Join Date
    Jan 2019
    Posts
    10
    Quote Originally Posted by lerxstrulz View Post
    I got it working, but was still having some tonal quality issues and then had to put it down for a while.

    In the meantime, the Audio lib has been updated with a pitch shifter that works pretty well. It does have some artifacting depending upon settings.
    Could you tell me where in the library is the feature? please. I have searched it and I can't find it.

  12. #12
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    19,922
    The only pitch shifting currently in the audio library is inside the granular effect.

    https://www.pjrc.com/teensy/gui/?inf...EffectGranular

    If you're expecting a beautiful sound of frequency domain shifting with special algorithms for detecting & handling percussion, prepare yourself to be underwhelmed. It sometimes does work surprisingly well for certain material if you tune the grain size well. Sometimes. Many other times, the effect is rather, well... granular.

  13. #13
    Senior Member
    Join Date
    Feb 2017
    Posts
    275
    Quote Originally Posted by PaulStoffregen View Post
    The only pitch shifting currently in the audio library is inside the granular effect.
    Many other times, the effect is rather, well... granular.
    But it makes a great voice changer. Sounds just like those mob people interviewed on TV who don't want to use their real voice or show their face

  14. #14
    Senior Member duff's Avatar
    Join Date
    Jan 2013
    Location
    Las Vegas
    Posts
    947
    I've been working on a phase vocoder off and on for awhile now and I can tell you its not easy, well not easy for realtime pitch shifting using Audio library.

    First off the q15 data type for CMSIS FFT's will lose to much spectral information so when finally doing the inverse FFT it's already added quite a bit of noise to the signal. I had to convert the q15 audio data to f32 data and work on it there. That right there means you have to have a micro that has a FPU, again for real time applications.

    Another issue is 75% (every 256 data points) overlap is not sufficient in my tests, that corresponds to doing a FFT and IFFT every other data block in the Audio library. I found the 87.5% (every 128 data points) overlap is the bare minimum. That corresponds to FFT, IFFT every block of data. I'm using the CMSIS f32 1024 point FFT's by the way. This also means that doing the Analysis and Synthesis stages have to be done in a single block time (~2.9ms) if you want to send the processed data out to other Audio Library modules.

    Another is that using 1024 point FFT means your frequency range is limited on resolution and low end frequencies. So for me I wanted to use it with a bass guitar and it won't work with say an open low E note (41.2Hz). So if your using it for say multiple instruments or such the low frequencies get real phasey or static sounding and won't be very pleasant to your ears It just can't reconstruct those low end frequencies which means you have to use larger point FFT that intern adds to much latency also! A 1024 point FFT is about 23ms of latency.

    Lastly the buffers needed to hold intermediary stages data become quite big, not a deal breaker but do eat up a lot of precious RAM.

    I have read a bunch of scholarly papers that claim to do realtime pitch shifting but they use novel tricks in the frequency domain that I either don't understand or are special case implementations of the vocoder such as only able to shift correctly on the frequencies that align exactly with the bin frequency of the FFT or such. You can do pitch sifting in time domain but I have not investigated this yet (SOLA). When I finally got a Not Real-Time version working and ported it to the Audio library, on my teensy 3.5 at a 144MHz it had a processor usage of over 250%! So I don't even know what it sounds like I'm hoping the T4 has power to do this but haven't tried it out because the issue with how the RAM is sectioned, plus I moved on to other things so hopefully in the future I can get back to this once the T4 api is more stable.

  15. #15
    Senior Member
    Join Date
    Oct 2015
    Location
    Vermont, USA
    Posts
    161
    If you're willing to use a teensy 3.5 or 3.6, you can use flowing point FFTs, which will make this much more feasible, given that we're just hobbyists.

    I've done a formant shifter on the Hoxton Owl, which is a similar class of ARM M4F as the Teensy 3.6. Between being floating point and focusing only on formants (instead of pitch) it sounded really smooth (not like the mobster informant on TV).

    With a formant shifter, you can gender bend voices or pretend you've been breathing helium. It's fun.

    I've been hoping to port it over to Teensy someday. Maybe I can make some time to do that.

    Chip

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •