There are definitely some things you can do to improve your code but because you need state tracking for the buttons you have the added benefit of already using the best method for debouncing buttons. You just need to turn your state tracking into a state machine by using a switch/case statement.
I was thinking a button array, possibly.
That's the method I used in the keypad driver. You can save a lot of space by using the bitRead() and bitWrite() functions found on the Arduino Reference page.
All the examples I see on the web do not send just a single report when pushing the button down and then another on the release.
That's generally a problem caused by not using a state machine or state tracking. They can't tell when a button has actually changed so the code gets out of hand very quickly.
Any ideas or better methods to reduce code? I am also worried about SRAM usage.
Yes, but I'm at work right now so this will be a bit short to start with.
You need to move your state tracking into a separate function that takes the button's previous state and pin number as variables so you can do all your state checking/updating only when called.
As I said above, create a bitMap array of any type of variable that can be used by bitRead() and bitWrite() and use that to store your states for each button.
That will help clean up your code but I would wait until you are through making everything work before you try to reduce ram usage.