I
announced some time ago that I had gotten a Wii, and the other day, I finally found
Rock Band for it (they released it back in June, but unfortunately they didn't ship it with French manuals so they didn't get it in Quebec at the major retailers, had to go to a smaller game shop to get it).
One thing I noticed about it is that all the instruments are USB devices. So of course, the geek in me wonders, "Hmm, maybe I could hook that up to my computer...". So I tried it. I used the drums, since they're my favourite instrument in the game.
To get Linux to recognize it was simple for me. I just plugged it in. You can try it for yourself, type "lsusb" with the drums plugged in, and when they're not. You should see a line that disappears. I get this:
Bus 002 Device 006: ID 1bad:0005
It doesn't say what it is, but that's no biggie.
UPDATE (Nov. 13, 2008): In Intrepid 64-bit, the joystick no longer works. If you're having problems,
click here. Also I've written
a follow-up post if you actually want to download something to look at.
Now the hard part is to get it to work with your programs (it's not actually that hard). The funny thing is that the drum kit is actually a joystick, at least as far as the computer knows. In effect, the little D-pad is the joystick and all the drums/buttons/pedal are buttons. So you can use anything that interfaces with a joystick in order to use the drums in your program. I used
SDL for this, which is a library for simple access to devices.
In order to use SDL to access your drums, you need a platform that supports SDL. Fortunately SDL is supported on a wide variety of platforms, so you should be OK. For this tutorial I'll be talking about Ubuntu Linux with g++, as that is what I used.
Next, you need a language with bindings to SDL. This is a lot of languages. I used C++, but you could write it in Haskell if you wanted to.
I make no guarantees about the drum kits for other systems like PS3 or XBox 360. I'm using the Wii version. I also don't know if it will give the same values for Windows or Mac OSX (don't see why it wouldn't).
So let's start. We'll do a bit of initialization:
#include <SDL.h>
#include <SDL_mixer.h>
#include <iostream>
using namespace std;
int main(){
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0){
cout << "Something messed up. :(" <<endl;
exit(1);
}
atexit(SDL_Quit);
What this does is initalizes SDL. We want to initialize the joystick so that we can access the drums, we want to initialize video so we can create a window (for some reason the events were messed up when I didn't have a window) and you'll want sound if you actually want to play sounds with your drums. Then we tell the system to call SDL_Quit when the program exits. This is good to clean up after ourselves.
Note that I include the SDL_mixer library, this makes it a lot easier to play sounds.
Now we want to initialize the joystick. It's really easy. I'm going to skip error checking and assume you only have one joystick plugged in and that it will work the first try. If you have multiple joysticks plugged in (or devices that pretend to be joysticks, like the drums), you can use SDL_NumJoysticks() to see how many you have. Then you can iterate over them and call SDL_JoystickName() to see the name of the joystick. Choose the one that says "Nintendo Wii Drum Kit" or something like that in it.
//here I assume you have the joystick at 0,
//if not replace it with a different number
SDL_Joystick * joystick = SDL_JoystickOpen(0);
//make it so that events are triggered for joysticks
SDL_JoystickEventState(SDL_ENABLE);
Ok, so our joystick is enabled. Let's load some sounds now. You can see the docs for the SDL_mixer functions
here if you want to know what they're doing.
Mix_Chunk * sounds[5];
const char * waves[] = {"bass.wav", "crash.wav", "hihat.wav",
"snare.wav", "kick.wav"};
if (Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 4096) != 0){
cout <<"Couldn't open audio device: " <<Mix_GetError()
<<endl;
exit(1);
}
for (int i = 0; i < 5; i++){
sounds[i] = Mix_LoadWAV(waves[i]);
if (sounds[i] == NULL)
cout <<"Loading sound " <<waves[i] <<" failed:"
<<Mix_GetError() <<endl;
}
Here we set 5 sounds to load. This corresponds to the 5 buttons for the drum kit. I put it in an order that I used on the drums once upon a time, but you can do whatever you want. You could even make it beep or quack when you hit one.
We make a call to Mix_OpenAudio(), which initializes the mixer, tells it what frequency we want, how many channels, etc. This lets us make sound.
We then load in all the WAV data into 5 sounds with a call to Mix_LoadWAV().
One last thing to initialize. We need to create a window:
SDL_Surface * window = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);
Done. We have initialized everything, now let's get ready to rock! We need to set up an event handling structure and check for drum events. The way the drums work is it sends a number for each button. The numbers are like this:
0: blue
1: green
2: red
3: yellow
4: foot pedal
There is one catch though. The numbers 0-3 also correspond to the 1, A, B and 2 buttons respectively so to differentiate, the drum kit also sends a 7 every time you hit one of the pads (but not the pedal). So when you hit the blue one, you actually receive two buttons: a 0, then a 7.
We need to do a little magic to keep track of what button was hit. I do this by recording the last button that was hit, and then when a 7 is received, I know that it was a pad and that I should play a sound (unless it was a 4, in which case I don't wait for a 7 after).
Let's set up the event loop:
SDL_Event evt;
bool loop = true;
int last_btn = 0;
while (loop){
if (SDL_PollEvent(&evt)){ //check for event
if (evt.type == SDL_QUIT){
//close button, quit
loop = false;
}else if (evt.type == SDL_JOYBUTTONDOWN){
//joystick button hit, let's play
if (evt.jbutton.button == 7) //pad
Mix_PlayChannel(-1, sounds[last_btn], 0);
else if (evt.jbutton.button == 4) //pedal
Mix_PlayChannel(-1, sounds[4], 0);
else
last_btn = evt.jbutton.button;
}
}
}
In our example, we handle events by polling. There are other ways to do events with SDL, but this is the best way for games in my opinion.
We check to see if there is an event by calling SDL_PollEvent(). If there is, the function returns true and puts the info into our evt structure.
There are only two events we care about, SDL_QUIT and SDL_JOYBUTTONDOWN (we could use the joystick axes events to check to see if people used the D-pad, but we just want to play drums). If we get a quit event, then we just tell the loop to stop going and then go on with it. If it is a joystick button down event, it means someone hit something. We then want to check to see what was hit. If it is a 7, it means we have just hit a pad and the last_btn variable holds whatever pad it was. We make a call to Mix_PlayChannel() to play our WAV file.
If the button was a 4, it was the pedal, so play the pedal sound (which should be at sounds[4]).
Finally, if it was anything else, just store the value and be done with it.
Now we need to clean up:
for (int i = 0; i < 5; i++)
Mix_FreeChunk(sounds[i]);
Mix_CloseAudio();
// remember to use the same number here
// as you did with SDL_JoystickOpen()
SDL_JoystickClose(0);
And we're done! To compile with g++ you use the following command, assuming your code was in drums.cpp:
g++ `pkg-config --libs --cflags sdl` -lSDL_mixer drums.cpp -o drums
Then:
./drums
Now you should get a little black window pop up, and when you have it selected your drums should play sounds (so long as you have the WAV files in the same folder as the executable, you can get some good tracks
here).
So yeah,
Frets on Fire with drums anyone?
Let me know if you have any questions.
Update Nov. 12, 2008: Intrepid uses a HAL that doesn't use the Wii Drums properly. To fix it, you have to tell X what to do. However, keep in mind that if you don't follow these directions to the letter, you may run into big problems.
First, install the driver:
sudo apt-get install xserver-xorg-input-joystick
Backup your xorg.conf file:
cd /etc/X11
sudo cp xorg.conf xorg.conf.bak
You'll need to see what the kernel is loading your joystick as. Do this:
lshal > before (with drums not plugged in)
lshal > after (with drums plugged in)
diff before after | grep event
This will output some stuff about the drums. You should see somewhere that it says /dev/input/event6 or something with a different number (event4, event5, etc.). Remember this number.
Next you'll need to make some tweaks to xorg.conf. Type:
gedit xorg.conf
You'll see a bunch of commented out sections, uncomment those (except for the actual comments). Before those add this, replacing event6 with whatever you had above:
Section "ServerFlags"
Option "AutoAddDevices" "False"
EndSection
Section "InputDevice"
Identifier "Configured Joystick"
Driver "joystick"
Option "Device" "/dev/input/event6"
EndSection
If you don't know what you're doing, write the following down on a piece of paper:
sudo cp /etc/X11/xorg.conf.bak /etc/X11/xorg.conf
If you mess up, this will get your system back. If things die and you can't log in, press Ctrl+Alt+F1 to switch to a terminal, log in normally, and type the stuff above. Then restart your computer.
Log out and log in again (assuming all went well) and you should be able to use the drums as a joystick again.