Jump to content

TimeWalker75a

Member
  • Posts

    2
  • Joined

  • Last visited

  • Country

    Latvia
  • Donations

    0.00 USD 

Posts posted by TimeWalker75a

  1. @@TimeWalker75a

    Sorry it took so long for me to notice your post, but there you go:

    https://www.dropbox.com/s/96tsou813nt3o51/sketch_footswitch.ino?dl=1

     

     

     

    // #include <Mouse.h>
    // #include <Keyboard.h>
    #include "MIDIUSB.h" // Allows Arduino to act as a MIDI instrument
    
    #include <HID-Project.h>
    // The HID library which produces the multimedia keys
    //(Volume UP,DOWN and MUTE)  https://github.com/NicoHood/HID
    
    
    /* 
     Sketch for a USB-controlled guitar footswitch with five buttons and LEDs
      - for the use in Rocksmith 2014
        * via USB keyboard emulation
      - for the use with AmpliTube 4
        * via MIDI signals
      - for the use in Reaper (Digital Audio Workstation)
        * via USB keyboard emulation (shortcuts assigned within Reaper)
      - for the use as a regular GamePad
        * HID library makes Windows recognize it
    
      Please note: Enable the serial monitor in the Arduino program to
                   get feedback on the application modes and footswitch modes.
        
      Version 7:
        - Complete rework, added AmpliTube & Reaper support
      Version 8:
        - Used MIDIUSB for direct midi device emulation
        - Implemented media keys and GamePad with HID-Project
      Version 8.1: 
        - Added round() in map_exponential() for intended conversion from double to int
      Version 8.2:
        - Added more GamePad buttons
      Version 8.3:
        - Make Max Dynamic Difficulty last longer
      Version 8.4:
        - Added Reaper Mode 2 "Multitrack navigation"
     */
    
     
    /* 7-Pin connection used to connect 
    FAME 5-Button Footswitch with ERNIE BALL Volume Pedal:
    
    Connection Board: 1 to 10 
    Cable:            0 to 7
    
    Board  1 - Cable 0, 1 - 5V - Yellow
    Board  2 - Cable 0, 2 
    Board  3 - Cable 0, 3 - Analog POT1 - Red
    Board  4 - Cable 0, 2
    Board  5 - Cable 0, 4 - GND - Black
    Board  6 - Cable 0, 2
    Board  7 - Cable 0, 5
    Board  8 - Cable 0, 2
    Board  9 - Cable 0, 6 
    Board 10 - Cable 0, 2
    
    __________________________________________
    Arduino     - Wire   - Cable - Wire (Poti)
    ------------------------------------------
    5V          - Yellow - 1     - orange
    Analog POT1 - Red    - 3     - red
    GND         - Black  - 4     - brown
    
    */
     
    // --- Constants ---
    // Pedal's potentiometer analog input pin:
    #define POT1 0
    // Tolerance for detecting a change in 'pedalState'
    #define POT_tol 5 // 3 gives light flickering near position zero (4 is better)
    #define POT_max 1023 // minimum potentiometer value (fixed)
    #define POT_min 0    // maximum potentiometer value (fixed)
    
    // Pin connections for switches (pushbuttons) on the Leonardo board
    // Please note: Edit this part! Most likely you connected the buttons and LEDs 
    // to different pins ;-)
    #define BUTTON1 7
    #define BUTTON2 3
    #define BUTTON3 4
    #define BUTTON4 8
    #define BUTTON5 5
    // Pin connections for LEDs on the Leonardo board (use PWM pins to allow analogWrite)
    #define LED1 6
    #define LED2 9
    #define LED3 10
    #define LED4 11
    #define LED5 13
    // Default flash time in milliseconds
    #define FLASHDELAY 100
    
    // application modes
    #define Rocksmith 1
    #define AmpliTube 2
    #define Reaper 3
    #define GamePad 4
    
    // --- Variables ---
    // Reference array to the buttons and LEDs
    int buttons[5]     = {BUTTON1, BUTTON2, BUTTON3, BUTTON4, BUTTON5};
    int LEDs[5]        = {LED1, LED2, LED3, LED4, LED5};
    // Array for checking the state of a pushButton (HIGH or LOW)
    int buttonState[5] = {HIGH, HIGH, HIGH, HIGH, HIGH};
    
    // State of the pedal (0...1023)
    bool pedal_is_connected = true; // Is a pedal connected?
    int pedalState = 0;
    int analogLEDvalue = 0; //(0...255)
    
    // Counter for the number of times a button has been pressed
    int buttonCounter[5] = {0, 0, 0, 0, 0};
    
    // Generally, you shuould use "unsigned long" for variables that hold time
    // The value will quickly become too large for an int to store
    
    // interval during which the button counters can be increased (milliseconds)
    unsigned long buttonTimer[5] = {1500, 1500, 1500, 1500, 750};
    // will store last time LED was updated
    unsigned long previousMillis[5] = {0, 0, 0, 0, 0}; 
    
    // --- RS Mode 2: Riff Repeater ---
    const char* RS_mode_2_keys[4] = {"9", "0", "o", "p"}; // Rocksmith 2014
    // const char* RS_mode_2_keys[4] = {"[", "]", "-", "+"}; // Rocksmith 2014 Remastered
    
    // --- RS Mode 3: Menu Navigation ---
    KeyboardKeycode RS_mode_3_keys[4] = {KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW};
    
    
    
    
    // Initial application mode:
    int previous_application_mode = -1;
    int application_mode = 0;
    // int application_mode = Rocksmith;
    // int application_mode = AmpliTube;
    // int application_mode = Reaper;
    // int application_mode = GamePad;
    
    
    
    // Footswitch mode (submodes within an application mode)
    int footswitch_mode = 0;
    int previous_footswitch_mode = -1;
    
    // Rocksmith:
    // * Mode 0: Initial State (to avoid output when connected and perform LED check)
    // * Mode 1: Tone Selection
    // * Mode 2: Riff Repeater
    // * Mode 3: Menu Navigation
    //
    // AmpliTube:
    // * Mode 0: Initial State + Setup (start AmpliTube)
    // * Mode 1: Looper
    // * Mode 2: Control Change (Stomp A)
    // * Mode 3: Program Change
    // * Mode 4: More Control Change
    // * Mode 5: Record, Play/Pause, Loop
    //
    // Reaper:
    // * Mode 0: Initial State
    // * Mode 1: Rewind, Play/Stop, Record, Metronome
    // * Mode 2: Go to previous marker, Play/Pause, Go to next marker, Metronome
    
    // GamePad:
    // * Mode 0: Initial State
    // * Mode 1: Send Windows GamePad button events 1 to 4
    //           and an analog signal when the pedal is used
    
    // Store the selected tone globally, so that it can be reused when switching back
    // to Rocksmith Mode 1
    int Rocksmith_tone_selection = 1; 
    
    // AmpliTube program selection, is also stored globally:
    int AmpliTube_program_selection = 0;
    int AmpliTube_recording = 0;
    int AmpliTube_playing = 0;
    
    // Reaper global variables
    int Reaper_recording = 0;
    int Reaper_playing = 0;
    int Reaper_metronome = 0;
    
    // Windows system variables
    int system_volume = 0;
    
    // ##################################################################################
    //                                       SETUP
    // ##################################################################################
    void setup() {
      // Setup Buttons and activation LEDs
      for(int currentButton = 0; currentButton < 5; currentButton++ ) {
        pinMode(buttons[currentButton], INPUT);          // Set pin for Button
        pinMode(LEDs[currentButton],   OUTPUT);          // Set pin for LED
        // Set every saved button state to its actual current state:
        buttonState[currentButton] = digitalRead(buttons[currentButton]);
      }
      
      Serial.begin(57600);    // Set MIDI baud rate
      Keyboard.begin();       // Initialize control over the keyboard  
      LEDs_perform_check();   // Run LED check  
      Consumer.begin();       // Initialize HID media buttons
      
      // Sends a clean report to the host. This is important on any Arduino type.
      Gamepad.begin();
      
      // Check if a pedal board is connected
      pedal_is_connected = Pedal_perform_check();
    }
    
    // ##################################################################################
    //                                    MAIN LOOP
    // ##################################################################################
    void loop() {
      if (application_mode == 0){
        run_application_selection();    
        LEDs_dim_analog(); // Control brightness of LEDs with potentiometer  
      }
      else if (application_mode == Rocksmith){
        run_application_Rocksmith();
      }
      else if (application_mode == AmpliTube){
        run_application_AmpliTube();
      }
      else if (application_mode == Reaper){
        run_application_Reaper();
      }
      else if (application_mode == GamePad){
        run_application_GamePad();
      }
      send_status(); // Send status information to PC via serial connection
      
      previous_application_mode = application_mode;
      previous_footswitch_mode  = footswitch_mode;
      
      delay(5); // Wait some milliseconds for the next loop to prevent double inputs...  
    }
    
    // ##################################################################################
    //                                     METHODS
    // ##################################################################################
    
    // ---------------------------------------------------------------------------------
    //            Application Selection: Switch between the different applications
    // ---------------------------------------------------------------------------------
    void run_application_selection(){
      for(int currentButton = 0; currentButton < 5; currentButton++) {          
        if (digitalRead(buttons[currentButton]) != buttonState[currentButton]){
          switch( currentButton ) {
            case 0: // Mode Rocksmith
              application_mode = Rocksmith;
              footswitch_mode = 0;
              break;
            case 1: // Mode AmpliTube
              application_mode = AmpliTube;
              footswitch_mode = 0;
              break;
            case 2: // Mode Reaper
              application_mode = Reaper;
              footswitch_mode = 0;
              break;
            case 3: 
              application_mode = GamePad;
              footswitch_mode = 0;
              break;
            case 4: break;
          }      
        }
        buttonState[currentButton] = digitalRead(buttons[currentButton]);
      }
    }
      
    // ---------------------------------------------------------------------------------
    //                        Application mode: ROCKSMITH
    // ---------------------------------------------------------------------------------
    void run_application_Rocksmith(){
      
      for(int currentButton = 0; currentButton < 5; currentButton++) {          
        if (digitalRead(buttons[currentButton]) != buttonState[currentButton]){
          
          switch ( footswitch_mode ) {
            case 1:{ // --- RS Mode 1: Tone Selection ---
              switch( currentButton ) {            
                case 4:
                  perform_mode_selection();
                  break;
                default:
                  Rocksmith_tone_selection = currentButton + 1;
                  Keyboard.print(Rocksmith_tone_selection);     // Keyboard output                
                  LED_light_single(currentButton);    // LED control
                  break;
              }
              break;
            }
            case 2:{ // --- RS Mode 2: Riff Repeater ---
              switch( currentButton ) {
                case 4:   perform_mode_selection(); break;
                default:
                  Keyboard.print(RS_mode_2_keys[currentButton]);
                  // Serial.print(RS_mode_2_keys[currentButton]); // debugging
                  break; // Keyboard output
              }
              break;
            }
            case 3:{ // --- RS Mode 3: Menu Navigation ---          
              switch( currentButton ) {   
                case 4:
                  perform_mode_selection();
                  break;
                default:
                  Keyboard.write(RS_mode_3_keys[currentButton]);   // Keyboard output
                  buttonCounter[currentButton]++;
                  button_hold_or_release(currentButton); // Initiate holding button   
                  break;
              }
              break;
            }
            case 6:{ // --- Mode 6: Special macros ---
              switch( currentButton ) {
                case 0:
                  Rocksmith_set_music_volume(0);
                  footswitch_mode = 3;
                  break;            
                case 1:
                  Rocksmith_set_music_volume(1);
                  footswitch_mode = 3;
                  break;
              }
              break;
            }
            case 9:{ // --- Mode 9: Application Selection ---
              application_mode = 0;
              break;
            }
          }
        }
        buttonState[currentButton] = digitalRead(buttons[currentButton]);    
      }
      execute_mode_selection();
      
      // Pedal section:
      // Only trigger an action if the pedalState has changed
      int pedalState_new = Pedal_read_state(); // Update pedalState
      if (pedalState_new != pedalState){
        switch ( footswitch_mode ) {
          case 3:{ // --- RS Mode 3: Menu Navigation --- 
            // Option 1: Use arrow keys
            // if (pedalState_new > pedalState){
              // Keyboard.write(KEY_UP_ARROW);
            // }
            // else{
              // Keyboard.write(KEY_DOWN_ARROW);
            // }
            
            // Option 2: Use GamePad joystick
            int axis_pos_int = map_exponential(pedalState_new, POT_min, POT_max, 0, 10000);
            int axis_pos     = map(axis_pos_int, 0, 10000, 32767, -32768);
            Gamepad.yAxis(axis_pos);
            Gamepad.write();
            break;
          }
          default:{
            control_volume(); // Control Windows system volume
          }
        }
        pedalState = pedalState_new; // Update pedalState
      }
    }
    
    
    
    // ---------------------------------------------------------------------------------
    //                        Application mode: AmpliTube
    // ---------------------------------------------------------------------------------
    void run_application_AmpliTube(){
      
      for(int currentButton = 0; currentButton < 5; currentButton++) {          
        if (digitalRead(buttons[currentButton]) != buttonState[currentButton]){
          
          switch ( footswitch_mode ) {
            case 1:{ // --- Mode 1: Looper ---
              switch( currentButton ) {
                case 0: AmpliTube_bypass_on_off(currentButton, 90); break;
                case 1: AmpliTube_bypass_on_off(currentButton, 91); break;
                case 2: AmpliTube_bypass_on_off(currentButton, 92); break;
                case 3: AmpliTube_bypass_on_off(currentButton, 93); break;
                case 4: perform_mode_selection(); break;
              }        
              break;
            }
            case 2:{ // --- Mode 2: Control Change (e.g. Stomp A) ---
              switch( currentButton ) {
                case 0: AmpliTube_bypass_on_off(currentButton, 26); break;
                case 1: AmpliTube_bypass_on_off(currentButton, 27); break;
                case 2: AmpliTube_bypass_on_off(currentButton, 28); break;
                case 3: AmpliTube_bypass_on_off(currentButton, 29); break;
                case 4: perform_mode_selection(); break;
              }
              break;
            }
            case 3:{ // --- Mode 3: Program Change ---
              switch( currentButton ) {
                case 0: 
                  midi_program_change(1);
                  AmpliTube_program_selection = currentButton;
                  LED_light_single(currentButton);
                  break;
                case 1: 
                  midi_program_change(2);
                  AmpliTube_program_selection = currentButton;
                  LED_light_single(currentButton);
                  break;
                case 2: 
                  midi_program_change(3);
                  AmpliTube_program_selection = currentButton;
                  LED_light_single(currentButton);
                  break;
                case 3: 
                  midi_program_change(4);
                  AmpliTube_program_selection = currentButton;
                  LED_light_single(currentButton);
                  break;
                case 4: perform_mode_selection(); break;
              }
              break;
            }
             case 4:{ // --- Mode 4: More Control Change ---
              switch( currentButton ) {
                case 0: 
                  // Must send "off" and "on" simultaneously for some reason
                  midi_control_change(74, 0);
                  midi_control_change(74, 127);
                  LED_flash(currentButton,FLASHDELAY);
                  break;
                case 1:
                  // Must send "off" and "on" simultaneously for some reason
                  midi_control_change(75, 0);
                  midi_control_change(75, 127);
                  LED_flash(currentButton,FLASHDELAY);
                  break;
                case 2: AmpliTube_bypass_on_off(currentButton, 33); break;
                case 3: AmpliTube_bypass_on_off(currentButton, 34); break;
                case 4: perform_mode_selection(); break;
              }
              break;
             }
             case 5:{ // --- Mode 5: Record, Play/Pause, Rewind ---
              switch( currentButton ) {
                case 0: // Record (starting recording starts playing, too)
                  AmpliTube_bypass_on_off(0, 42);
                  AmpliTube_recording = !AmpliTube_recording;
                  if (AmpliTube_playing == 0) AmpliTube_playing = 1;
                  break;
                case 1: // Play (Pause ends recording)
                  AmpliTube_bypass_on_off(1, 43);
                  AmpliTube_playing = !AmpliTube_playing;
                  if (AmpliTube_recording == 1) AmpliTube_recording = 0;
                  break;
                case 2: // Rewind (only, when not recording)
                  if (AmpliTube_recording == 0){
                    AmpliTube_bypass_on_off(2, 44);
                    LED_flash(2, 500);
                  }
                  break;
                case 3: AmpliTube_bypass_on_off(currentButton, 45); break;
                case 4: perform_mode_selection(); break;
              }
              break;
             }
            case 9:{ // --- Mode 9: Application Selection ---
              application_mode = 0;
              break;
            }
          }
        }
        buttonState[currentButton] = digitalRead(buttons[currentButton]);
        
      }
      execute_mode_selection();
      
      // This must only be done after the loop through all buttons:
      if (footswitch_mode == 5){ // --- Mode 5: Record, Play/Pause, Rewind ---
        digitalWrite(LEDs[0], AmpliTube_recording);
        digitalWrite(LEDs[1], AmpliTube_playing);
      }
      
      // Pedal section:
      // Only trigger an action if the pedalState has changed
      int pedalState_new = Pedal_read_state(); // Update pedalState
      if (pedalState_new != pedalState){
        switch ( footswitch_mode ) {
          case 1:{ // --- Mode 1: Looper ---                
            // int send_value = map(pedalState_new, POT_min, POT_max, 0, 127);
            int send_value = map_exponential(pedalState_new, POT_min, POT_max, 0, 127);
            midi_control_change(107, send_value);
            break;
          }
          case 2:{ // --- Mode 2: Control Change (e.g. Stomp A) ---
            // int send_value = map(pedalState_new, POT_min, POT_max, 0, 127);
            int send_value = map_exponential(pedalState_new, POT_min, POT_max, 0, 127);
            midi_control_change(107, send_value);
            break;
          }
          case 3:{ // --- Mode 3: Program Change ---
            control_volume(); // Control Windows system volume
            break;
          }
          case 4:{ // --- Mode 4: More Control Change ---
            control_volume(); // Control Windows system volume
            break;
          }
          case 5:{ // --- Mode 5: Record, Play/Pause, Rewind ---
            // int send_value = map(pedalState_new, POT_min, POT_max, 0, 127);
            int send_value = map_exponential(pedalState_new, POT_min, POT_max, 0, 127);
            midi_control_change(107, send_value);
            break;
          }
        }
        
        pedalState = pedalState_new; // Update pedalState
      }
      
      
    }
    
    
    
    // ---------------------------------------------------------------------------------
    //                        Application mode: Reaper
    // ---------------------------------------------------------------------------------
    void run_application_Reaper(){
      
      for(int currentButton = 0; currentButton < 5; currentButton++) {          
        if (digitalRead(buttons[currentButton]) != buttonState[currentButton]){
          
          switch ( footswitch_mode ) {
            case 1:{ // --- Mode 1: Rewind, Play/Pause, Record, Metronome ---
              switch( currentButton ) {
                case 0: // Rewind
                  Keyboard.write(KEY_HOME);
                  LED_flash(currentButton, 500);
                  break;
                case 1: // Play/Pause
                  if (Reaper_playing == 0){
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('k'); // "Play" function
                    Keyboard.releaseAll();
                  }
                  else{
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    // Keyboard.press('l'); // "Pause" function
                    Keyboard.press('i'); // "Stop" function
                    Keyboard.releaseAll();
                  }
                  Reaper_playing = !Reaper_playing;
                  break;
                case 2: // Record (starting recording starts playing, too)
                  if (Reaper_recording == 0){
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press('r');
                    Keyboard.releaseAll();
                  }
                  else{
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('i');
                    Keyboard.releaseAll();
                  }
                  if ((Reaper_playing == 0) && (Reaper_recording == 0)){
                    Reaper_playing = 1;
                  }
                  else if ((Reaper_playing == 1) && (Reaper_recording == 1)){
                    Reaper_playing = 0;
                  }
                  Reaper_recording = !Reaper_recording;
                  break;
                case 3: // Metronome (enable / disable)
                  if (Reaper_metronome == 0){
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('o');
                    Keyboard.releaseAll();
                  }
                  else{
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('p');
                    Keyboard.releaseAll();
                  }
                  Reaper_metronome = !Reaper_metronome;
                  break;
                case 4:
                  perform_mode_selection();
                  break;
              }
              break;
            }
            case 2:{ // --- Mode 2: Multitrack navigation ---
              switch( currentButton ) {
                case 0: // Go to previous marker/project start
                  Keyboard.press(KEY_LEFT_CTRL);
                  Keyboard.press(KEY_LEFT_ALT);
                  Keyboard.press(KEY_LEFT_ARROW);
                  Keyboard.releaseAll();
                  LED_flash(currentButton, 50);
                  break;
                case 1: // Play/Pause
                  if (Reaper_playing == 0){
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('k'); // "Play" function
                    Keyboard.releaseAll();
                  }
                  else{
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('l'); // "Pause" function
                    // Keyboard.press('i'); // "Stop" function
                    Keyboard.releaseAll();
                  }
                  Reaper_playing = !Reaper_playing;
                  break;
                case 2: // Go to next marker/project end
                  Keyboard.press(KEY_LEFT_CTRL);
                  Keyboard.press(KEY_LEFT_ALT);
                  Keyboard.press(KEY_RIGHT_ARROW);
                  Keyboard.releaseAll();
                  LED_flash(currentButton, 50);
                  break;
                case 3: // Metronome (enable / disable)
                  if (Reaper_metronome == 0){
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('o');
                    Keyboard.releaseAll();
                  }
                  else{
                    Keyboard.press(KEY_LEFT_CTRL);
                    Keyboard.press(KEY_LEFT_SHIFT);
                    Keyboard.press('p');
                    Keyboard.releaseAll();
                  }
                  Reaper_metronome = !Reaper_metronome;
                  break;
                case 4:
                  perform_mode_selection();
                  break;
              }
              break;
            }
            case 9:{ // --- Mode 9: Application Selection ---
              application_mode = 0;
              break;
            }
          }
        }
        buttonState[currentButton] = digitalRead(buttons[currentButton]);
        
      }
      execute_mode_selection();
      
      // This must only be done after the loop through all buttons:
      if (footswitch_mode == 1){ // --- Mode 1: Record, Play/Pause, Rewind ---
        digitalWrite(LEDs[1], Reaper_playing);
        digitalWrite(LEDs[2], Reaper_recording);
        digitalWrite(LEDs[3], Reaper_metronome);
      }
      if (footswitch_mode == 2){ // --- Mode 2: Multitrack navigation ---
        digitalWrite(LEDs[1], Reaper_playing);
        digitalWrite(LEDs[3], Reaper_metronome);
      }
      
      // Pedal section:
      // Only trigger an action if the pedalState has changed
      int pedalState_new = Pedal_read_state(); // Update pedalState
      if (pedalState_new != pedalState){
        switch ( footswitch_mode ) {
          case 1:{ // --- Mode 1 ---                
            int send_value = map_exponential(pedalState_new, POT_min, POT_max, 0, 127);
            midi_control_change(107, send_value);
            break;
          }
          case 2:{ // --- Mode 2 ---                
            int send_value = map_exponential(pedalState_new, POT_min, POT_max, 0, 127);
            midi_control_change(107, send_value);
            break;
          }      
        }
        
        pedalState = pedalState_new; // Update pedalState
      }
    }
    
    
    
    // ---------------------------------------------------------------------------------
    //                        Application mode: GamePad
    // ---------------------------------------------------------------------------------
    void run_application_GamePad(){
      
      if (footswitch_mode == 0){
        LEDs_perform_check();
        footswitch_mode = 1;
      }
      
      for(int currentButton = 0; currentButton < 5; currentButton++) {          
        if (digitalRead(buttons[currentButton]) != buttonState[currentButton]){
          
          switch( currentButton ) {
            case 4: {
              perform_mode_selection();
              break;
            }
            default: {
              GamePad_button_toggle(currentButton);
              break;
            }
          }        
        }
        buttonState[currentButton] = digitalRead(buttons[currentButton]);    
      }
      execute_mode_selection();
      
      
      // Pedal section:
      // Only trigger an action if the pedalState has changed
      int pedalState_new = Pedal_read_state(); // Update pedalState
      if (pedalState_new != pedalState){
        
        // My custom exponential mapping does not work with negatives, so we need two steps:
        // 65535 is the decimal of the hex value 0xFFFF
        int axis_pos_int = map_exponential(pedalState_new, POT_min, POT_max, 0, 10000);
        int axis_pos = 0;
        
        switch(footswitch_mode){
          case 1:  
            axis_pos = map(axis_pos_int, 0, 10000, 32767, -32768);
            Gamepad.yAxis(axis_pos); 
            break;
          case 2:  
            axis_pos = map(axis_pos_int, 0, 10000, 32767, -32768);
            Gamepad.xAxis(axis_pos); 
            break;
          case 3:  
            axis_pos = map(axis_pos_int, 0, 10000, -128, 127);
            Gamepad.zAxis(axis_pos); 
            break;
          default: Gamepad.yAxis(axis_pos); break;
        }    
        // Functions above only set the values. This writes the report to the host.
        Gamepad.write();
        
        pedalState = pedalState_new; // Update pedalState
        
        // Serial.print("pedalState_new = ");
        // Serial.print(pedalState_new);
        // Serial.print(" axis_pos_int = ");
        // Serial.print(axis_pos_int);
        Serial.print(" axis_pos = ");
        Serial.print(axis_pos);
        Serial.print("\n");
      }
      
      
    }
    
    
    
    // ---------------------------------------------------------------------------------
    //                           Small help functions
    // ---------------------------------------------------------------------------------
    
    // --- Button 5 is tapped fast enough: Increase counter ----------------------------
    void perform_mode_selection(){   
      if(millis() - previousMillis[4] < buttonTimer[4]) {
        // Save time of last tap on button 5  
        previousMillis[4] = millis();
        // increase the counter of button 5 by one    
        buttonCounter[4]++;
        // Send status information to PC via the serial port
        Serial.print("Performing mode selection... ");
        Serial.print(buttonCounter[4]);
        Serial.print("\n");
    //    LED_light_single(4);
        for (int i = 0; i < 5; i++){
          if (buttonCounter[4] <= 5){
            if (i+1 <= buttonCounter[4]) digitalWrite(LEDs[i], HIGH);
            else                         digitalWrite(LEDs[i], LOW);
          }
          else{
            LEDs_turn_all(HIGH);
            digitalWrite(LEDs[buttonCounter[4]-6], LOW);
          }
        }
        delay(100);
        LEDs_turn_all(LOW); // turn off all LEDs    
      }
    }
    
    // --- Tapping on button 5 has stopped: Execute command ----------------------------
    // Execute a different functionality, based on how often button 5 has been pressed
    // before (and depending on the current footswitch_mode):
    void execute_mode_selection(){  
      if(millis() - previousMillis[4] >= buttonTimer[4]) {
        switch (application_mode){
          // For application_mode "Rocksmith":
          // 1: Toggle between footswitch_modes 1 and 2 (Tone Selection and Riff Repeater)
          // 2: mode = 1: - ESC
          //              - mode -> 3
          //    mode = 2: - DELETE (Escapes Riff Repeater)
          //              - mode -> 1
          //    mode = 3: - ESC
          // 3: mode = 3: - SPACE (Opens Menu)
          //    else:     - mode -> 3
          // 4: mode = 1: - Macro: Maximize Dynamic Difficulty
          //    else:     - mode -> 2
          // 5:           - mode -> 1
          // 6-9:         - mode -> 6-9
          case Rocksmith: {        
            switch ( buttonCounter[4] ){
              case 0: break;
              case 1:{
                if (footswitch_mode == 1) {
                  Keyboard.print(" ");
                  footswitch_mode = 2;
                }
                else if (footswitch_mode == 2) {
                  Keyboard.print(" ");
                  footswitch_mode = 1;
                }
                else if (footswitch_mode == 3) Keyboard.write(KEY_RETURN);
                else footswitch_mode = 1;
                break;
              }
              case 2:{
                if (footswitch_mode == 1) {
                  Keyboard.write(KEY_ESC);
                  footswitch_mode = 3;
                }
                else if (footswitch_mode == 2) {
                  Keyboard.write(KEY_DELETE);
                  footswitch_mode = 1;
                }
                else if (footswitch_mode == 3) Keyboard.write(KEY_ESC);
                break;
              }
              case 3:{
                if (footswitch_mode == 3) Keyboard.print(" ");
                else footswitch_mode = 3;
                break;
              }
              case 4:{
                if (footswitch_mode == 1) Rocksmith_set_DynamicDifficulty_to_max();
                else footswitch_mode = 2;
                break;
              }
              case 5:  footswitch_mode = 1; break;
              case 6:  footswitch_mode = 6; break;
              // case 7:  footswitch_mode = 7; break;
              // case 8:  footswitch_mode = 8; break;
              case 9:  footswitch_mode = 9; break;
              default: footswitch_mode = 1;
            }    
          
            // --- Stuff we need to do at the end of each loop that did not increase
            // --- the counter of button 5 (regardless of what buttons were pressed)
            switch ( footswitch_mode ) {
              case 0: // --- Mode 0: Initialization ---
                LEDs_perform_check();
                footswitch_mode = 1;
                break;
              case 1: // --- Mode 1: Tone Selection ---
                LED_light_single(Rocksmith_tone_selection-1);
                break;
              case 2: // --- Mode 2: Riff Repeater ---
                digitalWrite(LEDs[0], HIGH);   
                digitalWrite(LEDs[1], HIGH);
                digitalWrite(LEDs[2], LOW); 
                digitalWrite(LEDs[3], LOW);   
                digitalWrite(LEDs[4], LOW);
                break;
              case 3: // --- Mode 3: Menu Navigation ---
                digitalWrite(LEDs[0], HIGH);   
                digitalWrite(LEDs[1], HIGH);
                digitalWrite(LEDs[2], HIGH); 
                digitalWrite(LEDs[3], LOW);   
                //digitalWrite(LEDs[4], LOW);
                break;
            }
          break;
          }
          
          // For the application_mode "AmpliTube", simply switch to the footswitch_mode
          // corresponding to the number of button 5 presses:
          case AmpliTube: {
            switch ( buttonCounter[4] ){
              case 0:  break;
              case 1:  footswitch_mode = 1; break;
              case 2:  footswitch_mode = 2; break;
              case 3:  footswitch_mode = 3; break;
              case 4:  footswitch_mode = 4; break;
              case 5:  footswitch_mode = 5; break;
              case 9:  footswitch_mode = 9; break;
              default: footswitch_mode = 1; break;
            }
            // --- Stuff we need to do at the end of each loop that did not increase
            // --- the counter of button 5 (regardless of what buttons were pressed)
            switch ( footswitch_mode ) {
              case 0: // --- Mode 0: Initialization ---
                LEDs_perform_check();
                // AmpliTube_run_setup(); // Setup was required for version 7
                footswitch_mode = 1;
                break;
              case 3: // --- Mode 3: Program Change ---
                LED_light_single(AmpliTube_program_selection);
                break;
            }
          break;
          }
          
          // For the application_mode "Reaper", simply switch to the footswitch_mode
          // corresponding to the number of button 5 presses:
          case Reaper: {
            switch ( buttonCounter[4] ){
              case 0:  break;
              case 1:  footswitch_mode = 1; break;
              case 2:  footswitch_mode = 2; break;
              // case 3:  footswitch_mode = 3; break;
              // case 4:  footswitch_mode = 4; break;
              case 9:  footswitch_mode = 9; break;
              default: footswitch_mode = 1; break;
            }
            // --- Stuff we need to do at the end of each loop that did not increase
            // --- the counter of button 5 (regardless of what buttons were pressed)
            switch ( footswitch_mode ) {
              case 0: // --- Mode 0: Initialization ---
                LEDs_perform_check();
                footswitch_mode = 1;
                break;
            }
          break;
          }
          
          // For the application_mode "GamePad", switch to the footswitch_mode
          // corresponding to the number of button 5 presses
          // AND also toggle the corresponding GamePad button:
          case GamePad: {
            switch ( buttonCounter[4] ){
              case 0:  break;          
              case 1:{
                if (footswitch_mode == 1){GamePad_button_toggle(4);}
                footswitch_mode = 1; 
                break;
              }
              case 2:{ 
                if (footswitch_mode == 2){GamePad_button_toggle(4);}
                footswitch_mode = 2; 
                break;
              }
              case 3:{ 
                if (footswitch_mode == 3){GamePad_button_toggle(4);}
                footswitch_mode = 3; 
                break;
              }
              case 4:{ 
                if (footswitch_mode == 4){GamePad_button_toggle(4);}
                footswitch_mode = 4; 
                break;
              }
              case 9:  footswitch_mode = 9; break;
              default: footswitch_mode = 1; break;
            }
            // --- Stuff we need to do at the end of each loop that did not increase
            // --- the counter of button 5 (regardless of what buttons were pressed)
            switch ( footswitch_mode ) {
              case 0: // --- Mode 0: Initialization ---
                LEDs_perform_check();
                footswitch_mode = 1;
                break;
            }
          break;
          }
          
          // If application mode was different than the above:
          default:{
            switch ( buttonCounter[4] ){
              case 9:  footswitch_mode = 9; break;
            }
          }
        }
        // --- Independent of the application_mode: ---
        // Reset counter of button 5
        buttonCounter[4] = 0;
        // Save time of last tap on button 5  
        previousMillis[4] = millis();
        
        
        // --- Reset application_mode if
        // footswitch_mode = "Mode 9: Application Selection" was selected ---
        if (footswitch_mode == 9){
          application_mode = 0;
          footswitch_mode = 0;
        }
    
      }
    }
    
    
    
    // --- Perform LED check, which includes blinking all LEDs -------------------------
    void LEDs_perform_check() {
      // Loop from first to last pin
      for (int LED = 0; LED < 5; LED++)  {LED_flash(LED, FLASHDELAY);}
      // loop from the last pin to the first:
      for (int LED = 4; LED >= 0; LED--) {LED_flash(LED, FLASHDELAY);}
      delay(200);
      LEDs_turn_all(HIGH);
      delay(500);
      LEDs_turn_all(LOW);
      delay(500);  
    }
    
    // --- Flash a single LED ----------------------------------------------------------
    void LED_flash (int LED, int flashDelay) {  
      digitalWrite(LEDs[LED], HIGH);  // turn the pin on
      delay(flashDelay);              // Wait for x milliseconds
      digitalWrite(LEDs[LED], LOW);   // turn the pin off
    }
    
    // --- Light a single LED, while turning all others off ----------------------------
    void LED_light_single(int LED_select){  
      for (int LED = 0; LED < 5; LED++) {
        if (LED == LED_select) {digitalWrite(LEDs[LED], HIGH);}
        else                   {digitalWrite(LEDs[LED], LOW); }
      }
    }
    
    // --- Turn all LEDs on or off by making the voltage LOW or HIGH -------------------
    void LEDs_turn_all(int HIGH_or_LOW){  
      for (int LED = 0; LED < 5; LED++) {    
        digitalWrite(LEDs[LED], HIGH_or_LOW);
      }
    }
    
    
    // --- Control brightness of LEDs with potentiometer -------------------------------
    void LEDs_dim_analog(){
      // Print status information to serial connection if value has changed:
      int pedalState_new = Pedal_read_state(); // Update pedalState
      if (pedalState_new != pedalState){
        // map it to the range of the analog out:
        analogLEDvalue = map(pedalState_new, POT_min, POT_max, 0, 255);
        // analogLEDvalue = map_exponential(pedalState_new, POT_min, POT_max, 0, 255);
        
        Serial.print("pedalState = ");
        Serial.print(pedalState_new);
        Serial.print("; analogLEDvalue = ");
        Serial.print(analogLEDvalue);
        Serial.print("\n");    
      }
      pedalState = pedalState_new; // Update pedalState    
      
      // change the analog out value:
      analogWrite(LED1, analogLEDvalue);
      analogWrite(LED2, analogLEDvalue);
      analogWrite(LED3, analogLEDvalue);
      analogWrite(LED4, analogLEDvalue);
      analogWrite(LED5, analogLEDvalue);
    }
    
    
    // --- Allow "holding" buttons if they are pressed often enough --------------------
    // A "held" button can be "released" by pressing any other button; The button
    // lights up while it is "held"
    void button_hold_or_release(int currentButton){
      // Pressed for the first time: Turn LED low
      if (buttonCounter[currentButton] == 1){
        previousMillis[currentButton] = millis();
        digitalWrite(LEDs[currentButton], LOW);
      }
      // Keyboard output: "press" holds the button, until "released"      
      if ((buttonCounter[currentButton] == 5)
      && (millis() - previousMillis[currentButton] < buttonTimer[currentButton])){    
        Keyboard.press(RS_mode_3_keys[currentButton]);   
        buttonCounter[currentButton]++;
        digitalWrite(LEDs[currentButton], HIGH);
        digitalWrite(LEDs[4], HIGH);
      }
      // "Release" all buttons
      if (buttonCounter[currentButton] > 6){
        Keyboard.releaseAll();
        // Reset button counter
        for (int i = 0; i<4; i++){
          buttonCounter[i] = 0;
        }
        digitalWrite(LEDs[currentButton], LOW);
        digitalWrite(LEDs[4], LOW);
      }
    }
    
    
    // --- Rocksmith 2014: Macro for setting Dynamic Difficulty to maximum -------------
    void Rocksmith_set_DynamicDifficulty_to_max(){
      Keyboard.print(" ");             delay(1000);
      for (int i=0; i<60;i++){
        Keyboard.print(RS_mode_2_keys[3]); delay(50);
      }
      for (int i=0; i<20;i++){
        Keyboard.print(RS_mode_2_keys[0]); delay(50);
      }
      // Keyboard.write(KEY_UP_ARROW);  delay(50);
      // Keyboard.write(KEY_DOWN_ARROW);  delay(50);
      Keyboard.write(KEY_DOWN_ARROW);  delay(50);
      Keyboard.press(KEY_RIGHT_ARROW); delay(4000);
      Keyboard.releaseAll();
      Keyboard.write(KEY_DELETE);
    }
    
    // --- Rocksmith 2014: Macro for making the music silent or loud -------------------
    // Navigates the menu and slides the volume-slider to the left or right, depending
    // on the input
    void Rocksmith_set_music_volume(int ON_or_OFF){
      Keyboard.print(" ");              delay(1000);
      Keyboard.write(KEY_UP_ARROW);     delay(50);
      Keyboard.write(KEY_UP_ARROW);     delay(50);
      Keyboard.write(KEY_UP_ARROW);     delay(50);
      Keyboard.write(KEY_UP_ARROW);     delay(50);
      Keyboard.write(KEY_DOWN_ARROW);   delay(50);
      Keyboard.write(KEY_DOWN_ARROW);   delay(500);
      Keyboard.write(KEY_RETURN);       delay(1500);
      Keyboard.write(KEY_RETURN);       delay(1500);
      if (ON_or_OFF == 0){
        for (int i=100; i>0;i--){
          Keyboard.write(KEY_LEFT_ARROW); delay(50);
        }
      }
      else if (ON_or_OFF == 1){
        for (int i=0; i<75;i++){
          Keyboard.write(KEY_RIGHT_ARROW); delay(50);
        }   
      }
      Keyboard.write(KEY_ESC);  delay(1500);
      Keyboard.write(KEY_ESC);  delay(3000);
      Keyboard.write(KEY_ESC);  delay(500);
      for (int i=0; i<7;i++){
        Keyboard.write(KEY_UP_ARROW); delay(50);
      }
    }
    
    // --- AmpliTube: Perform Setup -------
    void AmpliTube_run_setup(){ 
      // Start the program "Midi_to_Serial"
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.press(KEY_LEFT_ALT);
      Keyboard.press('s');
      delay(100);
      Keyboard.releaseAll();   delay(4000);
      Keyboard.write('A');     delay(100);
      Keyboard.write('E');     delay(100);
      Keyboard.write('B');     delay(100);
      Keyboard.write('D');     delay(100);
      Keyboard.write('B');     delay(100);
      Keyboard.press(KEY_LEFT_GUI);
      Keyboard.press(KEY_DOWN_ARROW);
      Keyboard.releaseAll();   delay(100);
      
      Keyboard.write(KEY_LEFT_GUI); delay(500);
      Keyboard.print("AmpliTube");  delay(1000);
      Keyboard.write(KEY_RETURN);   delay(100);
    }
    
    // --- AmpliTube: Toggle a bypass between states on and off (including LED) --------
    // These are "control change" MIDI signals
    void AmpliTube_bypass_on_off(int currentButton, char controlChange){
      if( buttonState[currentButton] == LOW ) {
        midi_control_change(controlChange, 0 ); // bypass off
        digitalWrite(LEDs[currentButton], HIGH);
      }
      else {
        midi_control_change(controlChange, 127 ); // bypass on
        digitalWrite(LEDs[currentButton], LOW);
      }
    }
    
    
    
    void midi_program_change(byte program) {
    // First parameter is the event type (0x0C = program change).
    // Second parameter is the event type, combined with the channel.
    // Third parameter is the program number (0-127).
    // Fourth parameter is just a dummy (0).
      int channel = 0; // channel is always "1": (0-15)
      uint8_t event_channel = 0xC0 | channel;
      midiEventPacket_t program_change = {0x0C, event_channel, program, 0};
      MidiUSB.sendMIDI(program_change);
      MidiUSB.flush();
    }
    
    void midi_control_change(byte control, byte value) {
    // First parameter is the event type (0x0B = control change).
    // Second parameter is the event type, combined with the channel.
    // Third parameter is the control number number (0-119).
    // Fourth parameter is the control value (0-127).
      int channel = 0; // channel is always "1": (0-15)
      uint8_t event_channel = 0xB0 | channel;
      // midiEventPacket_t control_change = {0x0B, 0xB0 | channel, control, value};
      midiEventPacket_t control_change = {0x0B, event_channel, control, value};
      // midiEventPacket_t event = {0x0B, channel, control, value};
      MidiUSB.sendMIDI(control_change);
      MidiUSB.flush();
    }
    
    
    
    // --- Send status information to PC via serial connection ------------------------
    void send_status(){
      if ((previous_application_mode != application_mode) ||
      (previous_footswitch_mode  != footswitch_mode)){
        Serial.print("application_mode = ");
        Serial.print(application_mode);
        Serial.print(", ");
        Serial.print("footswitch_mode = ");
        Serial.print(footswitch_mode);
        Serial.print("\n");        
      }
    }
    
    // --- Check if a pedal board is connected -----------------------------------------
    // Is supposed to be run during setup.
    // Returns a boolean which can be used to set "pedal_is_connected"
    bool Pedal_perform_check(){
      #define TRIES 10  
      #define PEDAL_CONNECTED_THRESHOLD 5 // (maximum difference in range of 100)
      
      // Read pedal state and map it to a range of 0 ... 100:
      int pedalState_mapped = map_exponential(Pedal_read_state(), POT_min, POT_max, 0, 100);
      int pedalState_mapped_new = pedalState_mapped;
      
      for (int TRY = 1; TRY <= TRIES; TRY++){
        delay(10); // Wait for x milliseconds
        
        pedalState_mapped_new = map_exponential(Pedal_read_state(), POT_min, POT_max, 0, 100); 
        
        int pedalState_diff = pedalState_mapped_new - pedalState_mapped;    
        pedalState_mapped = pedalState_mapped_new; // Update pedalState
        
        if (pedalState_diff < 0){ // make positive if negative
          pedalState_diff = -pedalState_diff;
        }
        
        Serial.print("TRY = ");
        Serial.print(TRY);
        Serial.print("; pedalState_diff = ");
        Serial.print(pedalState_diff);
        Serial.print("\n");    
        
        if (pedalState_diff > PEDAL_CONNECTED_THRESHOLD){
          Serial.print("Pedal failed test and is not connected!\n");
          return false;  // pedal failed test and is not connected
        } 
      }
      
      Serial.print("Pedal passed test and is connected.\n");
      return true; // pedal passed test and is connected
    }
    
    // --- Read the pedal's potentiometer ----------------------------------------------
    int Pedal_read_state(){
      if (pedal_is_connected == false){
        // Abort function and return the last known state of the pedal:
        return pedalState;
      }
      
      int pedalState_old  = pedalState;
      // int pedalState_read = analogRead(POT1);    // read the value from the sensor
      int pedalState_new  = pedalState;
      
      // The analogRead() tends to 'flicker' (giving unstable values), so we take the 
      // mean of a couple of reads:
      #define SAMPLES 10
      double pedalState_read = 0;
      for (int i=0; i< SAMPLES ; i++) pedalState_read += analogRead(POT1);
      pedalState_read /= SAMPLES;
      pedalState_read = round(pedalState_read);
      
      // Serial.print("pedalState_read = ");
      // Serial.print(pedalState_read);
      // Serial.print("\n");
      
      
      if ((pedalState_read <= pedalState_old-POT_tol) || 
          (pedalState_read >= pedalState_old+POT_tol)){
        pedalState_new = pedalState_read;    
      }  
      if (pedalState_read <= POT_min+POT_tol){
        pedalState_new = POT_min; // Minimum potentiometer value
      }
      if (pedalState_read >= POT_max-POT_tol){
        pedalState_new = POT_max; // Maximum potentiometer value
      } 
      
      return pedalState_new;
    }
    
    // --- Exponential mapping from one interval to another ----------------------------
    int map_exponential(double value, double x, double y, double a, double b){
      int    ret_i = 0;
      double ret_d = 0.0;
      
      // linear:
      // ret = (value-x)/(y-x)*(b-a) + a;
      
      // logarithmus naturalis:
      // if (value != 0){
        // double ln = log(value-x);
        // double ln_ref = log(y-x);
        // ret = ln/ln_ref * (b-a);
      // }
      
      // square root:
      double sqrt     = pow(value, 0.5);
      double sqrt_ref = pow(y-x,   0.5);
      ret_d = sqrt/sqrt_ref * (b-a);
      ret_i = round(ret_d);
    
    
      
      return ret_i;
    }
    
    // --- Control the computer volume with the media keys -----------------------------
    void control_volume(){
      
      int pedalState_new = Pedal_read_state(); // Update pedalState
      if (pedalState_new != pedalState) {
        
        int system_volume_new = map_exponential(pedalState_new, POT_min, POT_max, 0, 100);
        
        while (system_volume < system_volume_new) {
          Consumer.write(MEDIA_VOLUME_UP);
          system_volume = system_volume + 2;
        }    
        while (system_volume > system_volume_new) {
          Consumer.write(MEDIA_VOLUME_DOWN);
          system_volume = system_volume - 2;
        }
        
        Serial.print("system_volume = ");
        Serial.print(system_volume);
        Serial.print("\n");
        
        pedalState = pedalState_new; // Update pedalState
      }
      
    }
    
    
    void GamePad_button_toggle(int currentButton){
      // "press" + "release" means we toggle the button
      // takes the footswitch mode into account to use buttons above 5
      Gamepad.press(currentButton + 1 + 5*(footswitch_mode-1));
      Gamepad.write();
      LED_flash(currentButton, FLASHDELAY/2);
      Gamepad.release(currentButton + 1 + 5*(footswitch_mode-1));
      Gamepad.write();
    }
     

    No worries, I've my "waiting" threshold quite high ;) I appreciatr the link! It's grand you still use this and it appears to have turned into something bigger indeed!

     

    Sent from my SM-G920F using Tapatalk

    • Like 1
  2. @@KingCobra

    Actually I'm glad someone asked :)

     

    Here is my latest .ino version:

    https://dl.dropboxusercontent.com/u/8722253/Arduino/sketch_footswitch_08.ino

     

    I have continued working on my program and now use it for other applications and games!

    ...

     

    I also attached a volume pedal (by Ernie Ball) to the Arduino, specifically the pedal's potentiometer, to be able to send analog signals to the PC.

    I use this for Window's volume control and scrolling inside Rocksmith. But the main purpose was to enable the control of virtual pedal boards in AmpliTube 4.

     

    So... the program now has many more functions than you might need... or maybe you will like them, too! You can download a free trial version of AmpliTube 4, it works with the Rocksmith Cable.

     

    If you have questions or suggestions for improvement, please let me know!

     

    Hey there fellow DTP fan @@Azrael !

    It's been a year since the last post, hoping you are still using this. As such, I wonder if you have that sketch around as I'm looking to build something similar but appears Dropbox has decided to change public links with one of the recent updates, so I can't get ahold of it? Appreciate if you could re-link it.

×
×
  • Create New...

Important Information

By using this site, you agree to our Guidelines. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. - Privacy Policy