Jump to content

-= HOW TO BUILD A PROFESSIONAL ROCKSMITH Plug´n´Play USB / BT FLOORBOARD FOR TONECHANGE for less than 30$/€ =-


Rodman
 Share

Recommended Posts

-= HOW TO BUILD A PROFESSIONAL ROCKSMITH Plug´n´Play USB / BT FLOORBOARD FOR TONECHANGE for less than 30$/€ =-

Latest Update: 20.01.2015

http://i59.tinypic.com/29o51jd.jpg



This video is just a proof of concept, it is gonna edit: might, with a chance of slim to none be replaced by a better one as soon as my hardware hell has vanished … sigh…

Features (V2.0):
  • KEYBOARD INTERFACE – works as standard Keyboard – no special drivers, software or hacks needed
  • PLUG AND PLAY for Windows and Mac
  • USB or BLUETOOTH ver. available.
  • EASY TO BUILD - ONLY 5 Wires to SOLDER
  • CHEAP
  • INGAME TONESWITCH everywhere (LAS, SA, SESSION MODE, MENU….)

http://i59.tinypic.com/29o51jd.jpg
You want one of those –
Here is the DIY HOW TO:

Needed:

  • 4 Button Floorboard of your choice (no matter what connector it has) (20€ +)
  • A USB or BT Keyboard (use an old or get a defective one (Keys 1,2,3,4 gotta be working) (0-25€)
  • Soldering stuff incl.
  • 5 thin wires (e.g. an old IDE Cable)
  • Multimeter (optional but makes it easier)
  • A screwdriver
  • Gaffer Tape
  • A textmarker (for cd labeling)

http://i62.tinypic.com/390xy.jpg
Difficulty: easy

  • If you have sucessfully soldered anything before – you´re gonna do it for sure
  • If you have never soldered anything before – thats a rather easy task to start off

Time:
A few hours or less (my second version was done from scratch in 30 mins)


So here we go!
Grab your Keyboard (The HP one i used has a small interface that will fit in almost any footswitch, a long enough cable and a matrix that is easy to follow – hence the used button wiring is easy to determine).

-Unscrew and open it.

http://i58.tinypic.com/f00ua0.jpg

As in my pics below mark the buttons 1-4 (not on numpad) .
When you lift the rubber layer you see the wiring matrix.
Short explanation of keyboard function: 2 matrixes with wires – when pressed together = 2 pins on the interface board are connected and thats the buttons signal.
So – mark the buttons you wanna use on both layers of the matrix (waterproof marker for cd labeling works good)

Optional: if you have extra switches on the floorboard you might wanna use extra keys like: Enter, Esc, Up, Down – if so – mark them too!
http://i57.tinypic.com/eq1htt.jpg

Now for the hardest (not really) part:
Go to Button 1 and follow the wires (white lines) on both matrixes to the interface board. Double check, you can follow them with different color textmarkerst to make sure, but that shouldnt be needed, and is way too professional for my way of doing stuff like that ;)
Now you should have found the two pins on the interface board that handle „1“
(Alternative: search gxxgle for schematics / pinout dor your specific keyboard)
To test:







- I TAKE NO RESPONSIBILITY FOR DAMAGED HARDWARE OF YOURS -

Connect the usb plug to your computer, open a texteditor and shortcut the two pins that you have marked on the interface board – best with a multimeter set to resistance (ohm). Alt: use a piece of cable.
If everything is right – the text editor says: 1 – aint that great !

Now go on – follow the wires of button 2, 3 and 4 (and the extra buttons you might want)
It is very probable that you find a common ground for 1-4, so you should have five pins marked (i call em 1-4 and 0).
If there is no common ground = 8 pins determined (2 for each key) - no problem - read below!

http://i57.tinypic.com/10cixk5.jpg

Test all buttons the way above and if everything is ok you are ready to:
OPEN THE FLOORBOARD:


http://i60.tinypic.com/w7cs34.jpg
The board is gonna look a bit different for you unless you have the same 1st gen line 6 fb to work on...
Nevertheless - what you are searching for are the two pins for each buttons that are connected by a press on the button. The easiest way to find those is using a multimeter set to resistance (ohm) - adress 2 suspected pins - press the button and if the number on screen goes to 0 - these are the 2 pins to solder!

Furthermore: Check if there is a common ground (connection) between the individual buttons with the multimeter ("0" on screen means pins are connected and hence only one of them has to be wired to the ground of the keyboard interface)...
This is very probable, however if not (no "common ground" on fb board) you have 2 options:
#1 (recomended) connect one of the "to solder" pins of each button by soldering with the next one so only one of them has to be wired to "0" on keyboard interface. (see schematic diag 2)
#2 connect one of the "to solder" pins of each button by soldering with common ground of the keyboard interface - that way 8 wires to be soldered from kb interface to fbboard instead of 5.

If there is no "common ground" on kb interface, but you find 2 different pins for each key 1-4 just solder the 2 pins to the 2 pins of the fitting button in the floorboard board - in this cas you need to solder 8 wires (no problem)

If you have problems at any point pm me, post a pic of your situation, believe me - it is not gonna be so hard, i´m gonna help you.

When you are done you have:
- The pins on the floorboard to be wired
- the according pins on the keyboard interface sending "1" - "4"

WIRING
Solder the according pins (there are excellent youtube vids how to solder if you never have)

@@Frack 's little tips for this :

>>The parallels contacts material you'll solder is copper covered with a carbon like stuff.
you can remove this carbon by using the FLAT part of one screwdriver or a blade, take care to don't scratch the copper though.
This carbon is a pain in the a.. to solder (try on one unused contact first) go very gently with several passes.

Once the copper is visible & clean put some solder in it, the differents parallels contacts
should not be short circuited, if it happends, heat the solder along the copper to minimise the quantity.

put some solder on the end of the wires & heat both when in contact, blow on it to accelerate the cooler process.

take care to not overheat the board or the added wires (as they are very thin) any telecom/computer wire is ok for this.<<

If you look at my pics you can see that i did some scratching on the kb interface pins, put some solder on and got a nice result with that.

Here a principal schematic diagram:
http://i62.tinypic.com/2wlsd8n.jpg
The black dots are the common ground on the fb board = they show a resistance of zero when tested against each other - hence only one of them has to be wired to common ground on keyboard interface.

Schematic diagram 2 (no common ground on fb board)
http://i58.tinypic.com/3008jk1.jpg
connect pins 1-4 by soldering and check with multimeter(ohm): pin 1 against 4: = zero = success

Schematic diagram 3 (no common ground on keyboard interface):
http://i62.tinypic.com/2568gns.jpg
so if you found 2 pins for each key 1-4 (instead of 1 for each and a common ground = 5) you gotta solder 8 wires instead of 5 as shown above, meaning that the pins with the same color on kb int connected = keypress.


http://i62.tinypic.com/2bd0yx.jpg

http://i59.tinypic.com/rqyk4w.jpg

http://i60.tinypic.com/wsehg.jpg

http://i59.tinypic.com/2ahytfp.jpg
http://i60.tinypic.com/2po9icz.jpg

finally -
http://i57.tinypic.com/n133gp.jpg


TESTING
Plug in to USB on PC/Mac or connect with bluetooth -
http://i57.tinypic.com/25yuq85.jpg
start text editor, press the buttons - when you get:
http://i61.tinypic.com/hvnrx0.jpg
you might have to recheck :)

if you get:
http://i61.tinypic.com/2lw8y81.jpg
it´s good

If a button press results in multiple inputs (e.g. you press Button 1 and "134" is the output)
This is most probably a result of the conections on the board. If you don´t want to go into detail of the wiring of your board you might have to do the following:
http://i62.tinypic.com/ay1du.jpg
Just cut the lines leading away from your "to solder" pins with a flat screwdriver for each button.
This stops the fb from working with the amp it was designed for, but it can be rewinded by a simple solder point easily if ever needed.

Test again!



FINISHING

!!!!!!!UNPLUG!!!!!!!!!!!!!
Fit the keyboard interface into your fb, make sure there is no short circuiting (easiest way - cover whole thing with gaffer tape !!!)
http://i59.tinypic.com/1fhd8p.jpg
please take a little more time than i obviously did here :)

http://i61.tinypic.com/35ksw0l.jpg
I had to drill a hole to make the cable fit - i left the original connector on the other side as it was - so it can still be used with line 6 equipment :)

http://i61.tinypic.com/214w0h2.jpg

Test again!

ROCK

If you like it HIT THANKS
If you need support pm me – i´ll assist you.

YOU HAVE TO POST A PIC or VID of your FINISHED PROJECT IN THIS THREAT!!!!

Regards,
R

  • Like 33
Link to comment
Share on other sites

Awesome!

Even the current short version is a very detailed guide, seems easy enough to follow.

I'll post some pics too and what hardware I used, if I am succesful :)

  • Like 1

Check out my easy tutorial on how to add a metronome to your CDLC: Mute the original music, play only with the metronome and find out how good you really sound! Also: Find CDLCs that have the metronome enabled!

 

Want a USB-Footswitch that you can use to control the tone selection and all the menus in Rocksmith? Check out Rodman's Tutorial and my additions to that!  Footswitch_Logo.png

My CDLCs: Devin Townsend - Life, Deep Peace, Ih-Ah!, Deadhead; Farin Urlaub - Ok

My ideas for new features in Ignition, e.g. filters for Multitrack CDLC and Metronome CDLC.

Link to comment
Share on other sites

There photo with chart as i used now RS and pedals

P.s. Audiocard is ESI U46XL - selected default audio device in windows

And there must be active pickups on guitar or need setup compressor in gap between Di-Dox and RS cable for good recognition notes( for signal amplification after di-box)

http://i.imgur.com/YhtNYDj.jpg

  • Like 3
Link to comment
Share on other sites

There photo with chart as i used now RS and pedals

P.s. Audiocard is ESI U46XL - selected default audio device in windows

And there must be active pickups on guitar or need setup compressor in gap between Di-Dox and RS cable for good recognition notes( for signal amplification after dit-box)

http://i.imgur.com/YhtNYDj.jpg

Omg!!!

All i can say right now - nice paddles!!!!

 

 

Sent from my iPhone using Tapatalk

  • Like 2
Link to comment
Share on other sites

WIRING:

Solder the according pins.

I've got a little tips for this :

 

The parallels contacts material you'll solder is copper covered with a carbon like stuff.

you can remove this carbon by using the FLAT part of one crewdriver or a blade, take care to don't scratch the copper though.

This carbon is a pain in the a.. to solder (try on one unused contact first) go very gently with several passes.

 

Once the copper is visible & clean put some solder in it, the differents parallels contacts

should not be short circuited, if it happends, heat the solder along the copper to minimise the quantity.

 

put some solder on the end of the wires & heat both when in contact, blow on it to accelerate the cooler process.

 

take care to not overheat the board or the added wires (as they are very thin) any telecom/computer wire is ok for this.

  • Like 1

Link to comment
Share on other sites

WIRING:

Solder the according pins.

I've got a little tips for this ...

 

Thx man, thats the spirit!

 

Didnt go into detail in soldering cause lack of time, just went the sloppy way recomending youtube vids...

O.c. I put your tips in the guide, thx again!!!

Link to comment
Share on other sites

Glad you liked it Rodman, I've a little describe the process mainly for the carbon thing,

You can correct my spelling/gramatical mystakes too (thanks).

 

(I've used this mod 2 times before in year 2000 for playing at Quake 3 & some emulators with joystick, & more recently with real guitar to play on Fofix 4 years ago.)

Link to comment
Share on other sites

  • 2 weeks later...

I am so exited! I am (more or less) done with my very own Rocksmith Footswitch and it is AWESOME!

 

Thanks again to Rodman for this great idea and this detailed tutorial. I would like to share my experience with all of you, because I extended the idea.

When I told him about my plans for the footswitch, a friend of mine had the idea to use a Arduino microcontroller instead of a regular keyboard. At first I thought this was way too much effort, but then I realized the potential.

 

Footswitch with the first three LEDs alight, so it is currently in Mode #3:

IMG_6543s.jpg?psid=1

 

Here is a link to a folder with some more pictures: http://1drv.ms/1sls8Pi

 

So, what are the advantages of using the Arduino instead of a keyboard?

Arduino is a tool / platform / combination of both open source hardware and software. The microcontrollers have in- and output pins that can receive signals (e.g. closing an electrical circuit by pushing a botton) or send signals (e.g. lighting a LED by sending current through an output). When connected to a PC (via USB), they can also send signals to the computer, for example (emulated) keyboard signals. The code to control how the Arduino works is written in C - it is very easy, due to the number of tutorials available.

 

By writing a useful program and uploading it to the Arduino, I enabled my footswitch to do lots of things, not only send the keyboard keys 1, 2, 3 and 4.

With the 5th button on my footswitch, I can select different modes by counting how many times I pressed it. It works something like that:

 

In Mode 1 the buttons control the Tone Selection.

 

When I press button #5, Riff Repeater (Mode 2) is entered and the buttons become 9, p, o, 0.

Pressing #5 in Mode 2 once sends a "SPACEBAR" to start repeating, pressing #5 twice instead sends "DELETE", to exit Riff Repeater.

 

When #5 is pressed twice in Mode 1, it enters the Menu Mode (Mode 3) where the buttons become UP, DOWN, LEFT and RIGHT

Pressing #5 in Mode 3 once sends "ENTER" and twice sends "ESC". This allows to navigate through all the menues.

 

Pressing #5 four times or more at any point returns to Mode 1.

 

I light up the LEDs over the buttons to show which tone is currently selected, count how many times I press button #5 or show which mode I am currently in.

 

This explanation is not complete, but I hope you get the idea!

The next thing I want to implement is a "hold button" command in Menu Mode to be able to navigate through longer lists or change settings like volume more easily.

But at this point I can already control the whole game with nothing but my feet :D

(Edit 1 hour later: I think I found a good way. If I press a button several times in a certain amount of time, it is now set to be kept pressed until I press another button.)

 

So, what components did I use?

 

I bought the Fame FS500 5 button footswitch. Here is a link to a german store, where it only costs 24,90€.

http://www.musicstore.de/de_DE/EUR/Fame-FS500-5-fach-Fu-schalter-/art-GIT0010472-000?recId=51c07e20-9984-11e4-9d13-448a5bd44d45

 

It was the cheapest footswitch with 5 buttons that I could find on the internet, but the quality is very satisfying. The casing is completely made of metal (no plastic) and has a rubber bottom that prevents it from sliding around on the floor. It's also quite heavy, which feels good. When unscrewing the original 7 pin connector, the remaining hole is large enough to fit a usb plug through.

The soldering and electrical connections inside are done well. Infact due to the pin connector that was used, I was able to connect the Arduino to the buttons with only pin cables. Soldering was only required to separately control the LEDs.

 

The Arduino Board is a Arduino Leonardo (not all Arduinos can be used as emulated keyboards).

Here is one tutorial that shows how to send keystrokes with the push of a button:

http://arduino.cc/en/Tutorial/KeyboardMessage

And this one shows how to control a LED:

http://arduino.cc/en/Tutorial/Fade

 

Basically all I had to do was combine and extend those tutorials for 5 buttons and 5 LEDs. If you want to do it yourself those tutorials should tell you everything you need, so I won't do my own tutorial. But if anyone is interested, I am happy to share the code of my program!

You need a special (free) software and drivers for the Arduino. Compiling and uploading a new program to the Arduino and resetting it takes about 10 seconds.

But once you uploaded a program, emulating a keyboard works plug&play on any other PC (I tested Win 7 and 8), no drivers required.

 

 

As you can see, the possibilities are nearly endless. I can extend the functionality for Rocksmith, but of course I can also use the footwitch for any other purpose.

I could configure the buttons for use on the desktop or in other games and either upload a new program each time I want to use it for something else, or use the buttons to change the way it works.

 

This is the first time I have used an Arduino, and it is exiting to think about what completely different stuff can be done with those. Go check out the "What have you built with Arduino?" videos on their website ;)

 

Regards,

Azrael

  • Like 5

Check out my easy tutorial on how to add a metronome to your CDLC: Mute the original music, play only with the metronome and find out how good you really sound! Also: Find CDLCs that have the metronome enabled!

 

Want a USB-Footswitch that you can use to control the tone selection and all the menus in Rocksmith? Check out Rodman's Tutorial and my additions to that!  Footswitch_Logo.png

My CDLCs: Devin Townsend - Life, Deep Peace, Ih-Ah!, Deadhead; Farin Urlaub - Ok

My ideas for new features in Ignition, e.g. filters for Multitrack CDLC and Metronome CDLC.

Link to comment
Share on other sites

@@Azrael - Great job, man!

Thats the spirit!!!!!!

 

As stated above - i am waiting for a pic of your controller here!!!!!! ;)

Link to comment
Share on other sites

I already had a link to some pictures included, but I have put one into the post itself now, too :-)

(Can anyone tell me if there is a way to reduce the size of an image on this board?)

Check out my easy tutorial on how to add a metronome to your CDLC: Mute the original music, play only with the metronome and find out how good you really sound! Also: Find CDLCs that have the metronome enabled!

 

Want a USB-Footswitch that you can use to control the tone selection and all the menus in Rocksmith? Check out Rodman's Tutorial and my additions to that!  Footswitch_Logo.png

My CDLCs: Devin Townsend - Life, Deep Peace, Ih-Ah!, Deadhead; Farin Urlaub - Ok

My ideas for new features in Ignition, e.g. filters for Multitrack CDLC and Metronome CDLC.

Link to comment
Share on other sites

@@Azrael

 

Beauty !!!

 

Wonderful work - congrats!!!!

 

Reduce picsize - best way i know is to use tinypic.com - upload resized - u get the direct link including /img

 

 

Sent from my iPhone using Tapatalk

Link to comment
Share on other sites

  • 3 months later...
  • 1 month later...

The second I saw this thread I knew I was going to go spin a quick PCB for doing this with a cheap uController....

 

Stay tuned....

Just shout if you need any help!!!

 

 

Sent from my iPhone using Tapatalk

Link to comment
Share on other sites

  • 1 year later...

I am so exited! I am (more or less) done with my very own Rocksmith Footswitch and it is AWESOME!

 

 

Just ordered the necessary hardware for this (I chose a Leonardo Pro Micro ATmega32U4, hope that works), should be here by the end of the week.

 

Do you mind sharing your source code to give me a head start?

 

Thanks :)

Link to comment
Share on other sites

@@KingCobra

Actually I'm glad someone asked :)

 

Here is my latest .ino version:

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

Edit: New link

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

 

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

 

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

 

 

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!

  • Like 1

Check out my easy tutorial on how to add a metronome to your CDLC: Mute the original music, play only with the metronome and find out how good you really sound! Also: Find CDLCs that have the metronome enabled!

 

Want a USB-Footswitch that you can use to control the tone selection and all the menus in Rocksmith? Check out Rodman's Tutorial and my additions to that!  Footswitch_Logo.png

My CDLCs: Devin Townsend - Life, Deep Peace, Ih-Ah!, Deadhead; Farin Urlaub - Ok

My ideas for new features in Ignition, e.g. filters for Multitrack CDLC and Metronome CDLC.

Link to comment
Share on other sites

@@KingCobra

Actually I'm glad someone asked :)

 

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

 

@@Azrael

Thanks for this. I'll dig through the code at the weekend. Not sure when the pedal board will arrive though, hopefully before then. I was toying with the idea of adding a small led/oled display to allow for a basic menu. Not sure where it would fit, have you considered this?

 

Also, maybe you should consider creating you own thread.

Link to comment
Share on other sites

A friend of mine has some of those 2-line displays and once I toyed around with them to see if I could get them working. I did, and we even considered cutting the board up, but soon realized there was not enough space to fit it in...

You could make an external case and/or stick it to the backside of the board... but in the end I considered that to much effort. I know all the modes of my program and use the LEDs to give me hints where possible, so a display is not really necessary for me.

I did, however, integrate a lot of Serial.print() commands into the code. So you can pop up the serial printer and it shows you some of the stuff you would probably want to put on a display. Should help understand the functions ;-)

  • Like 1

Check out my easy tutorial on how to add a metronome to your CDLC: Mute the original music, play only with the metronome and find out how good you really sound! Also: Find CDLCs that have the metronome enabled!

 

Want a USB-Footswitch that you can use to control the tone selection and all the menus in Rocksmith? Check out Rodman's Tutorial and my additions to that!  Footswitch_Logo.png

My CDLCs: Devin Townsend - Life, Deep Peace, Ih-Ah!, Deadhead; Farin Urlaub - Ok

My ideas for new features in Ignition, e.g. filters for Multitrack CDLC and Metronome CDLC.

Link to comment
Share on other sites

  • 1 year later...

@@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.

Link to comment
Share on other sites

  • 2 months later...

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

 

 

Check out my easy tutorial on how to add a metronome to your CDLC: Mute the original music, play only with the metronome and find out how good you really sound! Also: Find CDLCs that have the metronome enabled!

 

Want a USB-Footswitch that you can use to control the tone selection and all the menus in Rocksmith? Check out Rodman's Tutorial and my additions to that!  Footswitch_Logo.png

My CDLCs: Devin Townsend - Life, Deep Peace, Ih-Ah!, Deadhead; Farin Urlaub - Ok

My ideas for new features in Ignition, e.g. filters for Multitrack CDLC and Metronome CDLC.

Link to comment
Share on other sites

@@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
Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

×
×
  • 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