Michael Feldman

CCRMA – Music 220C Project

 

Interactive Musical Games

 

Week 1 (note: weeks are relative i am not actually sure which week we were in at any given time.)—

Brainstorming Ideas for my project

General ideas – possibly working with interactive music making? Real-time composition? Real time performance? Game?

         Basically decided to work on a game.

         Mark Applebaum had already done a version of the piano composition I wanted to do and there are other problems with a serious 2-person musical instrument.

 

Week 2 –-

         Show 250A project –

         Decide what I want to work on.

         Where I am starting from:

Offence/defense

Not really creating music, just playing “samples” (not really samples, but it’s the same idea)

Requires listening room

        

         Goals:

                  Simultaneous offense/defense game

                  Does not use listening room.

                  Dynamic or interesting interaction between the players.

                  If possible make it real time – not turn by turn play (but not necessary)

        

 

Week 3 –-

Games ideas –

         2 ideas:

         (1) turn by turn

         like connect 4, except with 4 pitches instead of 4 spaces

         4x4 grid of 16 buttons

         the advantage of sound is that there can be a shift button, where the lights move and the pitch remains or the pitch moves and the light remains. Becomes a memory game as well at this point and more than just visual. Strategic – can set up a situation where a shift wins the game for you.

         (2) Dynamic control

         like a balance game where players are working to achieve a goal

         but competitive: one tries to create dissonance, the other consonance.

         A king-of-the-hill style game where if within realm of consonance the consonance person gets a point.

         Buttons to control whether the slider is controlling pitch, rhythm or chords, also button to shift whether you are controlling consonance or dissonance.

 

 

Week 4 –-

Chris would rather I move forward with the dynamic rather than turn-based game so I started to work on possible rules for a dynamic game.

 

         Sliders need to push and pull relative to starting point rather than in absolute space

         Players switch off between consonance and dissonance.

         One button to select intervals of a chord – slider shifts

         One button to select primary pitch – slider shifts

         One button to control rhythmic stability – slider balances (maybe the only one in absolute rather than relative space for the slider)

         LED shows who is winning or whether it’s in limbo. (light on player 1 side is on if more consonant, light on player 2 side is on if more dissonant. If a players light is on for a total of 10 seconds he/she wins a point.

        

         **Possible problem with consonance and dissonance being very close together.

 

Week 5 –-

Pd Patch work

I started by laying out the controls set-up

Two sliders each with 4 toggle switches. Then as I progressed to writing the code to make the sliders relative and to slide the chord tones apart, I created individual, self-contained blocks to simplify.

Week 6 –-

AVR board programming beginnings

         In changing the nature of the game I wish to design, I started with programming the AVR board.

         I set it up such that Pd can read the a2dvalues coming out of the board and so that the counter increments when the slider is within a certain range of the variable I have called “marble”

I worked on creating the basic physics for the game – getting the buttons to work and wiring up the board

I actually needed more inputs that the board had, so i multiplexed the LEDs. That made them flicker, so i multiplexed the buttons instead. In this way i created the basic functionality for the board (but with regular sliders (not haptic sliders)



Week 7 --

Construction

This week i constructed the hardware. Soldering all the buttons i needed to the wires and hooking them up.

I built the casing in the product realization lab and learned how to use the lasercad machine that i needed to use to cut the square holes in the acrylic for the sliders and square buttons.

I also mounted the haptic sliders and the motor board.



Week 9 --

Working with Michael Guerevich and Bill Verplank to get teh haptics on the sliders functioning

I wired them up, learned how to deal with the driver motors, (and how to fix it when all the wires fell out as they constantly did.




Week 10 --

Trying to compile the program.

I tried to compile teh haptic code, regular C code and pd code to finally construct the project.

I burned out one microprossesor and apparently taught some of teh systems staff about wonderful and new problems that the CCRMA computers could have.

I also created the basics for a second game – the balance game in which players have to identify different pitch spaces, by selecting them with the slider and button.



I have copied and pasted the c code below.





// compiler includes

#include <avr/io.h>

#include <avr/pgmspace.h>



// avrlib includes

#include "timer.h"

#include "global.h"

#include "uart.h"

#include "ccrma/osc.h"

#include "rprintf.h"

#include "spyglass.h"

#include "a2d.h"

#include "i2c.h"

#include "lcd.h"



//define motor things

#define DEBOUNCE_THRESHOLD 10



#define PWMB PD4

#define PWMA PD5

#define DIRA PD6

#define DIRB PD7



u08 Contrast=20; // spyglass lcd contrast

u16 d0, d1; // analog inputs 0-1023

s16 p0, p1, pos, f0, f1; // position with -512 to +511 (0 in middle)

u08 pwm;

s32 dutymax;

u16 duty; // output to pwm

u32 runs;

s16 forceout; // to motor

s32 W = 100; // width

s32 D = 60; // depth





//global variables for game

u08 buttonconf[8];

u08 startgame;

u08 buttonon[8];

u08 counter;



//function prototypes

u08 checkButton(u08 whichButton);

void setLED(u08 whichLED, u08 on);

void PERMsetLED(u08 whichLED, u08 on);





int main(void)

{

s32 a2dvalues[2]; // variable to store a/d value

u08 i;

u08 tmp;

s32 marble[2];

s32 bump[4];

u08 points[2];

u08 testbutton[8];

u08 bpoint[4];

s32 spoint[4];

u08 testpattern;

u08 p0yay[4];

u08 p1yay[4];

u08 ptotal[2];



//oscMessage stuff

uartInit();

oscInit();

timerInit();

uartSetBaudRate(115200);



//LCD Stuff

i2cInit();

spyglassInit();

spyglassLcdInit();

rprintfInit(spyglassLcdWriteChar);

lcdClear();

lcdHome();



//Initialize timer

timerInit();



// set PortD pins 6 and 7 to output

sbi(DDRD, PWMA);

sbi(DDRD, DIRA);

sbi(DDRD, PWMB);

sbi(DDRD, DIRB);



// set PWM resolution to low 8,9 or 10

pwm = 8;

dutymax = (1<<pwm) - 1;

timer1PWMInit(pwm);

timer1PWMAOn();

timer1PWMBOn();



// connect AVR to MotorDriver

#define PWMA PD5 // PortD pin 6 (blue) -> PortX pin 6

#define DIRA PD6 // PortD pin 7 (purple) -> PortX pin 7



//set ports A,B,C to a2d and buttons

a2dInit();



outb(DDRA, 0x00); // set low 3 pins to output for mux addressing and

// pins 4-7 as inputs for a2d conversion

outb(PORTA, 0x00); // set pull-ups to off



outb(DDRB, 0xE7); // set port B pins to output (For MUX ctl)

tmp=DDRC&0xFC;

outb(DDRC,0xFC|tmp); // set PORTC masking 0,1,7



timer0SetPrescaler(TIMER_CLK_DIV8);

timer1SetPrescaler(TIMER_CLK_DIV1);



lcdHome();

rprintf("Michael's Interface");

// allow for user to lift buttons after reset

lcdGotoXY(0,2);

rprintf("***wait for it...***");

timerPause(3000);

lcdGotoXY(0,2);

rprintf("Press1:Octave Slide ");

lcdGotoXY(0,3);

rprintf("Press2:Balancing Act");

//set startgame initial value

startgame = 0;

marble[0] = 0;

marble[1] = 0;

//wait for user to start program

while (startgame == 0){

counter++;

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

tmp = PORTB&0xF8; //Mask non-output for PORTB

outb(PORTB,i|tmp); //select MUX channel PORTB

setLED(i, checkButton(3)); //checkButton for Mux

buttonon[i] = checkButton(3);

if (buttonon[i] != testbutton[i]){

oscSendMessageIntInt(PSTR("/button"),i,buttonon[i]);

testbutton[i] = buttonon[i];

}

}

// hack for unknown button problem

if(buttonon[7]) {setLED(7,1);}

else {setLED(7,0);}

if (buttonon[0]){startgame = 1;}

if (buttonon[1]){startgame = 2;}

if (buttonon[4]){startgame = 1;}

if (buttonon[5]){startgame = 2;}



}



oscSendMessageInt(PSTR("/opening"),startgame);

timerPause(500);

srand(counter);

lcdGotoXY(0,1);

rprintf(" Ready? ");

timerPause(200);

lcdGotoXY(0,1);

rprintf(" ");

lcdGotoXY(0,2);

rprintf(" ");

lcdGotoXY(0,3);

rprintf(" ");

timerPause(3000);



while (1) { //press all 8 buttons to reset

if (!(buttonon[0] & buttonon[1] & buttonon[2] & buttonon[3] & buttonon[4] & buttonon[5] & buttonon[6] & buttonon[7])){

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

tmp = PORTB&0xF8; //Mask non-output for PORTB

outb(PORTB,i|tmp); //select MUX channel PORTB

setLED(i, checkButton(3)); //checkButton for Mux

buttonon[i] = checkButton(3);

if (buttonon[i] != testbutton[i]){

if(testbutton[i] = 1){

oscSendMessageIntInt(PSTR("/button"),i,buttonon[i]);

}

testbutton[i] = buttonon[i];

}

}

// hack for unknown button problem

if(buttonon[7]) {setLED(7,1);}

else {setLED(7,0);}



if(startgame == 1){

if(points[0]+points[1]<2){

lcdGotoXY(6,1);

rprintf("SliderGame");



if(buttonon[0]){

if(p0>(marble[0]-125)){

if(p0<marble[0]){

marble[0] = marble[0] - p0*0.5806 + 75.5806; //equation of the line where silder can

} //be within 125 and shoots it a max of 75

}

}

if(buttonon[1]){

if(p0>(marble[1]-125)){

if(p0<marble[1]){

marble[1] = marble[1] - p0*0.5806 + 75.5806; //equation of the line where silder can

} //be within 125 and shoots it a max of 75

}

}



if(buttonon[4]){

if(p0>(marble[0]+125)){

if(p0<marble[0]){

marble[0] = marble[0] + p1*0.5806 + 75.5806; //equation of the line where silder can

} //be within 125 and shoots it a max of 75

}

}



if(buttonon[5]){

if(p0>(marble[0]+125)){

if(p0<marble[0]){

marble[0] = marble[0] + p1*0.5806 + 75.5806; //equation of the line where silder can

} //be within 125 and shoots it a max of 75

}

}

oscSendMessageIntInt(PSTR("/marble"),marble[0],marble[1]);

lcdGotoXY(0,1);

rprintfNum(10, 4, TRUE, ' ', marble[0]);



//scoring points

if(marble[0]>250){ //****change this back to 500 once working

marble[0] = 1000;

PERMsetLED(3,1);

points[0] = points[0] + 1;

}

if(marble[0]<-500){

marble[0] = -1000;

PERMsetLED(7,1);

points[1] = points[1] + 1;

}

if(marble[1]>500){

marble[1] = 1000;

PERMsetLED(4,1);

points[0] = points[0] + 1;

}

if(marble[1]<-500){

marble[1] = -1000;

PERMsetLED(8,1);

points[1] = points[1] + 1;

}

}

else{

startgame =0;

oscSendMessageInt(PSTR("/opening"),startgame);

lcdGotoXY(0,0);

rprintf(" --GAMEOVER-- ");

lcdGotoXY(0,3);

rprintf("all buttons to reset");

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

tmp = PORTB&0xF8; //Mask non-output for PORTB

outb(PORTB,i|tmp); //select MUX channel PORTB

setLED(i, buttonon[i]); //checkButton for Mux

buttonon[i] = checkButton(3);

if (buttonon[i] != testbutton[i]){

oscSendMessageIntInt(PSTR("/button"),i,buttonon[i]);

testbutton[i] = buttonon[i];

}

}

if (points[0]>points[1]){

lcdGotoXY(0,1);

rprintf(" Player 1 Wins! ");

}

if (points[0]>points[1]){

lcdGotoXY(0,1);

rprintf(" Player 2 Wins! ");

}

else{

lcdGotoXY(0,1);

rprintf(" It's a Tie Game ");

}

if ((buttonon[0] & buttonon[1] & buttonon[2] & buttonon[3] & buttonon[4] & buttonon[5] & buttonon[6] & buttonon[7])){

points[0]=0;

points[1]=0;

main();

}

//**********************************************************************************

//second game - the balance game

if(startgame == 2){

if( (p0 != 3) & (p1 != 3)){

lcdGotoXY(6,1);

rprintf("BalanceGame");

if(testpattern != 1){

for(i=0;i<3;i++){

bpoint[i] = (rand() % 25) + 50;

spoint[i] = (bpoint[i]-62.5)/0.025;

oscSendMessageIntInt(PSTR("/bpoint"),i,bpoint[i]);

}

testpattern = 1;

}

if(buttonon[3]){

for(i=0;i<3;i++){

oscSendMessageIntInt(PSTR("/bpoint"),i,bpoint[i]);

}

}

if(buttonon[7]){

for(i=0;i<3;i++){

oscSendMessageIntInt(PSTR("/bpoint"),i,bpoint[i]);

}

}

for(i=0;i<3;i++){

if(buttonon[i]){

if(p1<(spoint[i]+100)){

if(p1>(spoint[i]-100)){

PERMsetLED(i,1);

p0yay[i] = 1;

}

}

}

}

for(i=4;i<7;i++){

if(buttonon[i]){

if(p1<(spoint[i]+100)){

if(p1>(spoint[i]-100)){

PERMsetLED(i,1);

p1yay[i] = 1;

}

}

}

}

} // end of p0 !=3.... loop

else{

startgame = 0;

oscSendMessageInt(PSTR("/opening"),startgame);

lcdGotoXY(0,0);

rprintf(" --GAMEOVER-- ");

lcdGotoXY(0,3);

rprintf("all buttons to reset");

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

tmp = PORTB&0xF8; //Mask non-output for PORTB

outb(PORTB,i|tmp); //select MUX channel PORTB

setLED(i, buttonon[i]); //checkButton for Mux

buttonon[i] = checkButton(3);

if (buttonon[i] != testbutton[i]){

oscSendMessageIntInt(PSTR("/button"),i,buttonon[i]);

testbutton[i] = buttonon[i];

}



ptotal[0] = p0yay[0] + p0yay[1] + p0yay[2];

ptotal[1] = p1yay[0] + p1yay[1] + p1yay[2];

}

if (ptotal[0] == 3){

lcdGotoXY(0,1);

rprintf(" Player 1 Wins! ");

}

if (ptotal[1] == 3){

lcdGotoXY(0,1);

rprintf(" Player 2 Wins! ");

}

else{

lcdGotoXY(0,1);

rprintf(" It's a Tie Game ");

}

if ((buttonon[0] & buttonon[1] & buttonon[2] & buttonon[3] & buttonon[4] & buttonon[5] & buttonon[6] & buttonon[7])){

main();

}

} //end of else loop

}

}// end of adding points if statement



} //end of game 1











bump[0] = 200;

bump[1] = -200;

// Haptic controls

p0 = a2dConvert10bit(0) - 512;

if (ABS(p0)>0)f0 = - p0*D/W;

if (ABS(p0)>W){

if(p0<0)f0 = ((p0 + 2*W)*D)/W;

else f0 = ((p0 - 2*W)*D)/W;

}

if (ABS(p0)>2*W)f0 = 0;

if (f0>0) cbi(PORTD, DIRA);

else sbi(PORTD,DIRA);

d0 = MIN(ABS(f0),dutymax);

timer1PWMASet(d0);



p1 = -(a2dConvert10bit(1) - 512);

if (ABS(p1)>0)f1 = p1*D/W;

if (ABS(p1)>W){

if(p1<0)f1 = -((p1 + 2*W)*D)/W;

else f1 = -((p1 - 2*W)*D)/W;

}

if (ABS(p1)>2*W)f1 = 0;

if (f1>0) cbi(PORTD, DIRB);

else sbi(PORTD,DIRB);

d1 = MIN(ABS(f1),dutymax);

timer1PWMBSet(d1);

oscSendMessageIntInt(PSTR("/a2dvalues"),p0,p1);





if(runs++%10==0){// read and display a2d A0 and A1

spyglassLcdGotoXY(0,2);

rprintf("p0:");

rprintfNum(10, 4, TRUE, ' ', p0);

rprintf(" f0:");

rprintfNum(10, 4, TRUE, ' ', f0);

spyglassLcdGotoXY(0,3);

rprintf("p1:");

rprintfNum(10, 4, TRUE, ' ', p1);

rprintf(" f1:");

rprintfNum(10, 4, TRUE, ' ', f1);

}

} // end of if statement

else {main();}

} // end of while(1) loop

a2dOff();

return 0;

}



u08 checkButton(u08 whichButton) {

return (! bit_is_set(PINB,whichButton));

/* Logical negation is because when the button is pushed, the pin

is drawn to ground, so the button is "on" when the bit is zero. */

}





void setLED(u08 i, u08 on) {

u32 msec[8];

u32 latetime[8];



if (on) {

//light the LED

msec[i] = 1389*(u32)timer0GetOverflowCount()/10000; //start each timer

if (buttonconf[i]==0){ //read button status

oscSendMessageIntInt(PSTR("/button"),i, 1);

if (i<3){sbi(PORTB,i+5);}

else {sbi(PORTC,i-1);}

buttonconf[i] = 1; //reset button status

}

}

else {

//turn off the LED

latetime[i] = ((1389*(u32)timer0GetOverflowCount()/10000) - msec[i]);

if (latetime[i] > 1000){ // set the amount of time the light stays on

if (buttonconf[i]==1){ //read button status

oscSendMessageIntInt(PSTR("/button"),i, 0);

if (i<3){cbi(PORTB,i+5);}

else {cbi(PORTC,i-1);}

buttonconf[i] = 0; //reset button status

}

}

}



}



void PERMsetLED(u08 i, u08 on) {



if (on) {

//light the LED

if (buttonconf[i]==0){ //read button status

oscSendMessageIntInt(PSTR("/button"),i, 1);

if (i<3){sbi(PORTB,i+5);}

else {sbi(PORTC,i-1);}

buttonconf[i] = 1; //reset button status

}

}



}