PDA

View Full Version : Dual Wiimote > Ableton Live 6 on a PowerBook G4



adoonan
02-06-2010, 07:41 AM
I love this program. I just created a preset that has two Wiimotes controlling an instance of Impulse. I'm not a drummer, but I've tried to recreate a drumset:


http://imgur.com/a8T3E.jpg


- A on the left hand Wiimote is the kick drum

- 'accel' on the left hand Wiimote is the snare


- A on the right hand Wiimote is closed Hi-Hat

- B on the right hand Wiimote is the open Hi-Hat

- 'accel' on the right hand Wiimote is a ride bell.


Frankly, it's exciting but useless. The lag is horrendous (and yes I am a life long musician so I'd like to think I have a decent sense of time). The lag can be as much as an eighth note, depending upon the tempo. Is my computer too slow? Are the Wiimotes not sensitive enough without MotionPlus? Am I just a horrible air drummer?


I'm hoping I can use this setup and other similar ideas to perform in a live setting. Any help will be extremely appreciated.

camille
02-06-2010, 04:34 PM
Hi Adoonan,


The problem of triggering notes with Wiimote's acceleration is actually quite complex.


The bad news:

Like you did, it is possible bind a MIDI Note event to the "accel" parameter but this won't work really well. First the lagging issue, but you could also experience unwanted triggerings, etc. Right now, the only good way to make a good trigger is to use Max/MSP or Pure Data and modify the accel signal so you have something usable as a trigger.


The good news:

There is perhaps a way to improve the lagging. Usually the audio interface doesn't have the right buffer size. Check in your virtual instrument or DAW the size of the audio buffer. If your computer has enough horse power, use 128 or 64 bytes. This will give you sufficiently low latency.


I'd like to add that although one can think that an accelerometer based instrument would be a natural choice for triggering rhythmic patterns, it is actually in my opinion a misconception: to have a precise timing you really need to hit something. If you play in the air, the is nothing to hit and the timing is shallow. Think of a chief orchestra: he moves his stick in the air, but there is no sound produced, only a shallow sense of tempo. But that is becoming philosophical, I should stop there …


Best,

Cam

adoonan
02-13-2010, 04:27 AM
I don't know what I changed but it seems to be working much better. I think I was just thrown off by not hitting anything, like you said. Might actually be doing this with a band for some songs.

camille
02-14-2010, 08:03 PM
awesome, let me know if you have fun.

Christopher Temple
06-22-2010, 11:09 PM
Hi guys.


I developed a bit of code using Processing that allows me to use the Wiimote as an air-drumstick. It's not anything like as responsive as a real drum, for the reasons Camille suggests above, but it's certainly usable. We use it live (YouTube to follow if we get our butts into gear) and it's a lot cheaper than buying all the expensive drums that we sample.


You'll need a working knowledge of both OSCulator and Processing to get this code up and running and your Wiimotes will need to be MotionPlus-equipped. I found I got best results by playing the Wiimotes upside down, with my index fingers extended along the reverse of the Wiimote, but whatever works for you ;-)



import oscP5.*;

import netP5.*;


OscP5 oscP5;

/* So the parameters here are as follows:

The first 4 are strings, indicating what Midi note you want the wimote to play

when:

1) - No buttons are pressed

2) - Button 1 is pressed

3) - Button 2 is pressed

4) - Buttons 1 and 2 are pressed.

I chose buttons 1 and 2 because I find the wiimotes are more responsive if they're

upside down.

The final parameter is a char, it needs to be either 'r' or 'l' depending on

wether you use the wiimote in your left or right hand.*/

Wiimote wiimote1 = new Wiimote("C0","D1","E1","F1", 'r');

Wiimote wiimote2 = new Wiimote("C0","D1","E1","F1", 'l');

NetAddress myRemoteLocation;


void setup() {

size(400,400);

frameRate(25);

oscP5 = new OscP5(this,9000);

myRemoteLocation = new NetAddress("127.0.0.1",8000);

/*On my setup I have OSC transmitting on port 9000 and receving on port 8000 hence

the values above. You'll need to change these to match your setup*/


//Attaching the OSC messages to the relevant methods:

oscP5.plug(this,"wii1velo","/wii/1/motion/velo");

oscP5.plug(this,"wii11","/wii/1/motion/velo");

oscP5.plug(this,"wii12","/wii/1/motion/velo");

oscP5.plug(this,"wii2velo","/wii/2/motion/velo");

oscP5.plug(this,"wii21","/wii/2/motion/velo");

oscP5.plug(this,"wii22","/wii/2/motion/velo");

}


//The methods for the above OSC messages:

void wii1velo(float p, float r, float y){wiimote1.update(p,r,y);}

void wii11(int x){wiimote1.button1change(x);}

void wii12(int x){wiimote1.button2change(x);}

void wii2velo(float p, float r, float y){wiimote2.update(p,r,y);}

void wii21(int x){wiimote1.button1change(x);}

void wii22(int x){wiimote1.button2change(x);}


void draw() {

background(0);

}


//This class represents the wiimote:

class Wiimote {

public int wiiNo;

public float[] anglev = new float[3];

public float[] lastanglev = new float[3];

public float[] llastanglev = new float[3];

public String note;

public String note1;

public String note2;

public String note12;

public int button1;

public int button2;

public int lr = 0;

int excludeCount = 0;

float minhit = 1;

float lastpeak;


Wiimote (String Note, String Note1, String Note2, String Note12, char LR) {

note = Note;

note1 = Note1;

note2 = Note2;

note12 = Note12;

if ( (LR == 'R') || (LR == 'r') ) lr = 1;

if ( (LR == 'L') || (LR == 'l') ) lr = -1;

if (lr == 0) println("Wiimote not initialised properly - Is it used in the left hand or the right hand?");

}


void button1change(int x)

{

button1 = x;

}


void button2change(int x)

{

button2 = x;

}


void update(float p, float r, float y)

{


anglev[0] = p;

anglev[1] = r;

anglev[2] = y;


if (excludeCount > 0) excludeCount++;

if (excludeCount > 5) excludeCount = 0;


/*This is the important bit.

Basically what this is saying is: If the wiimote has moved through the yaw angle

at speed greater than 1, and has has subsequently slowed down, then the WiiMote class should

regard this as a hit and send a OSC message*/

int hashit = 0;

if (lr == 1)

{

if ((lastanglev[2] - anglev[2] < 0) && (lastanglev[2] - llastanglev[2] < 0) && (lastanglev[2]< -minhit)) hashit = 1;

}


if (lr == -1)

{

if ((lastanglev[2] - anglev[2] > 0) && (lastanglev[2] - llastanglev[2] > 0) && (lastanglev[2]> minhit)) hashit = 1;

}


if (hashit == 1)

{

lastpeak = anglev[2];

float velocity = 0;

if (lr == 1) velocity = (lastpeak + minhit)/-(4.54 - minhit);

if (lr == -1) velocity = (lastpeak - minhit)/(4.54 - minhit);

if(velocity < 0) velocity = 0;

if (excludeCount == 0)

{

String notetoplay = "";

if ((button1 == 0) && (button2 == 0)) notetoplay = note;

if ((button1 == 1) && (button2 == 0)) notetoplay = note1;

if ((button1 == 0) && (button2 == 1)) notetoplay = note2;

if ((button1 == 1) && (button2 == 1)) notetoplay = note12;

sendNotePacket(velocity,midiToOSCPitch(notetoplay) );

excludeCount = 1;

}

}


//cleanup

llastanglev[0] = lastanglev[0];

llastanglev[1] = lastanglev[1];

llastanglev[2] = lastanglev[2];

lastanglev[0] = anglev[0];

lastanglev[1] = anglev[1];

lastanglev[2] = anglev[2];

}

}


/*This class sends an OSC message with 3 params labelled 'drumstick'

You'll need to set this up as a MIDI Note w/Params event type on OSCulator

The velocity parameter is between 0 and 1, which translates as between 0 and 127

in MIDI-speak. The pitch is a float value. Use the midiToOSCPitch method

to convert a Midi note to the corresponding OSC float value*/


void sendNotePacket(Float velocity, Float pitch) {

OscMessage resetMessage = new OscMessage("/drumstick");

resetMessage.add(0);

resetMessage.add(velocity);

resetMessage.add(pitch);

oscP5.send(resetMessage, myRemoteLocation);

OscMessage oscMessage = new OscMessage("/drumstick");

oscMessage.add(1);

oscMessage.add(velocity);

oscMessage.add(pitch);

oscP5.send(oscMessage, myRemoteLocation);

OscMessage oscMessageOff = new OscMessage("/drumstick");

oscMessageOff.add(0);

oscMessageOff.add(velocity);

oscMessageOff.add(pitch);

oscP5.send(oscMessageOff, myRemoteLocation);

}


float midiToOSCPitch(String midiNote) {

String[] noteLetters = {"C","D","E","F","G","A","B"};

float[] notePitches = {0.0, 2.0, 4.0, 5.0, 7.0, 9.0, 11.0};

float OSCPitch = 0;


//First match the octave

int matched = 0;

for(int i = -2; i < 9; i++) {

String[] m = match(midiNote, str(i));

if ( m != null) {

OSCPitch = -1 + (0.5*i);

matched = 1;

i = 9;

}

}


if (matched == 0) {

return 0.0;

}


//Then match the notes

for(int i = 0; i < 8; i++) {

String[] m = match(midiNote,noteLetters[i]);

if (m != null) {

float addPitch = notePitches[i]/24;

OSCPitch = OSCPitch + addPitch;

i = 8;

}

}

if (OSCPitch == 0) {

return 0.0;

}


//Next match the sharps or flats

float increment;

increment = 1.0/24.0;

if (match(midiNote,"#") != null) {

OSCPitch = OSCPitch + increment;

}

if (match(midiNote,"b") != null) {

OSCPitch = OSCPitch - increment;

}


return OSCPitch;

}

camille
06-22-2010, 11:27 PM
Hey Christopher,


Thanks for the code snippet !

I have permitted myself to add some quick formatting so it looks good.


Cam