//////////////////////////////////// // // TVSCV1S10, Gijs Gieskes 2011, http://gieskes.nl // TVout lib is needed: http://code.google.com/p/arduino-tvout/ // this one is with a mcp4912. // //////////////////////////////////// //digi pot vars #define SLAVESELECT 10 //ss #define DATAOUT 11 //MOSI #define DATAIN 12 //MISO - not used, but part of builtin SPI #define SPICLOCK 13 //sck byte pot[2] = { B00000000, B00010000}; #include #include "TVout.h" //#include //pollserial pserial; #define W 96 //was 128 #define H 94 #define OFFLEFT 64 #define ACTIV bitRead(PINB, 0) #define MODER bitRead(PINC, 0) #define SAVER bitRead(PINC, 4) #define SPEER bitRead(PIND, 1) //#define SPEER digitalRead(1) #define LENGR bitRead(PIND, 4) #define VOLUR bitRead(PIND, 6) #define COPYR bitRead(PIND, 5) #define BACKR bitRead(PIND, 3) #define FORWR bitRead(PIND, 2) TVout tv; //////////////////////////////// seq vars below int vol[8][16]; int prevol[2]; char length[8] = {15, 15, 15, 15, 15, 15, 15, 15}; char mlength[2] = {0, 0}; boolean mute[2] = {0, 0}; boolean patern[2] = {0, 0}; byte pss[2] = {0, 0}; byte tpss[2] = {0, 0}; byte aposition[2]; byte mposition[2]; byte mpatern[2][16]; byte off[2] = {0, 45}; byte dposition = 0; boolean resetpos[2] = {false, false}; boolean mresetpos[2] = {false, false}; char xypos[2] = {0, 0}; int curpos[2]; boolean displayMode; boolean curflip; boolean curani; byte curcount; char modSelectX = 0; char modSelectY = 0; char bpmspeed = 10; byte butDelay = 0; byte butCount = 0; int pvs = 100; byte prevBut = 100; byte recTimer = 0; boolean external = false; void setup() { //controls pinMode(0, INPUT); pinMode(2, INPUT); pinMode(3, INPUT); pinMode(4, INPUT); pinMode(5, INPUT); pinMode(6, INPUT); pinMode(14, INPUT); pinMode(18, INPUT); digitalWrite(0, HIGH); digitalWrite(2, HIGH); digitalWrite(3, HIGH); digitalWrite(4, HIGH); digitalWrite(5, HIGH); digitalWrite(6, HIGH); digitalWrite(14, HIGH); digitalWrite(18, HIGH); pinMode(8, INPUT); //trigger input //pal/ntsc pinMode(3, INPUT); digitalWrite(3, HIGH); delay(1); if(!digitalRead(3)){ tv.begin(NTSC, W, H); } else{ tv.begin(PAL, W, H); } tv.fill(0); //digi pot vars byte clr; pinMode(DATAOUT, OUTPUT); pinMode(DATAIN, INPUT); pinMode(SPICLOCK, OUTPUT); pinMode(SLAVESELECT, OUTPUT); digitalWrite(SLAVESELECT, HIGH); //disable device SPCR = (1<0; i--){ triggerADSR(); dposf(); updateVolumeOut(0, off[0]); updateVolumeOut(1, off[1]); tv.delay_frame(1); triggerADSR(); updateSelect(); updateVolume(0, off[0]); updateVolume(1, off[1]); tv.delay_frame(1); } } if(!MODER && !BACKR && !FORWR){ break; } } //clear screen //tv.fill(0); while(!MODER){ } //game sequencer while(1){ game(); if(!MODER){ break; } } } //trigger input used for resting to start so it can be used as ADSR envelope inline void triggerADSR(){ if(!ACTIV){ if(!displayMode){ resetpos[0] = true; resetpos[1] = true; } else{ mresetpos[0] = true; mresetpos[1] = true; resetpos[0] = true; resetpos[1] = true; } } } //display position render upadte, separate from sequence because of length inline void dposf(){ if(dposition < 15){ dposition++; } else{ dposition = 0; } } //update sequence position inline void updatePosition(byte inp, byte offset){ if(aposition[inp] < length[inp+pss[inp]] && !resetpos[inp]){ aposition[inp]++; } else{ resetpos[inp] = false; aposition[inp] = 0; if(mposition[inp] < mlength[inp] && !mresetpos[inp]){ mposition[inp]++; } else{ mresetpos[inp] = false; mposition[inp] = 0; } if(!displayMode){ updateLength(0, off[0]); updateLength(1, off[1]); } else{ //only auto change when in song mode tpss[inp] = mpatern[inp][mposition[inp]]; updatePattern(inp); } } //trigger out digitalWrite(1, aposition[0]); //upadte pattern select if(SPEER && COPYR && aposition[inp] == 0){ if(pss[inp] != tpss[inp]){ //reset volume display render position only on change //dposition = 0; pss[inp] = tpss[inp]; } } updatePosistionCursor(inp, offset); } //draw sequnce position indicator inline void updatePosistionCursor(byte inp, byte offset){ //clear old tv.draw_rect(0, 37+offset, OFFLEFT, 2, 0, 0); //draw sequence position indicator if(!displayMode){ //draw new tv.draw_rect(sizer(aposition[inp]), 37+offset, 2, 2, 1, 1); }else{ //draw the slower master position sequence //draw new tv.draw_rect(sizer(mposition[inp]), 37+offset, 2, 2, 1, 1); } } //draw sequence volume on position inline void updateVolume(byte inp, byte offset){ if(!displayMode){ //pattern mode //clear old tv.draw_rect(sizer(dposition), offset, 2, 31, 1, 1); //draw new tv.draw_rect(sizer(dposition), offset, 2, min(vol[inp+pss[inp]][dposition]>>5, 30), 0, 0); }else{ //song mode //clear old tv.draw_rect(sizer(dposition), off[inp], 2, 31, 0, 0); //draw new tv.draw_rect(sizer(dposition), off[inp]+((mpatern[inp][dposition]>>1)*8)+1, 2, 6, 1, 1); } } //draw sequence volume on position inline void updateVolumeOut(char inp, byte offset){ if(!mute[inp]){ //if volume changes are being made display the volume changes. if(!VOLUR && modSelectY == inp){ prevol[modSelectY] = vol[modSelectY+pss[modSelectY]][modSelectX]; }else{ prevol[inp] = vol[inp+pss[inp]][aposition[inp]]; } //clear old tv.draw_rect(OFFLEFT, offset+12, 27, 31, 1, 1); //draw new for(byte y=0; y<32; y++){ //use draw_row because it is fast. if(y >= 28){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-224), 0, 30)+OFFLEFT, 0); } else if(y >= 24){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-192), 0, 30)+OFFLEFT, 0); } else if(y >= 20){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-160), 0, 30)+OFFLEFT, 0); } else if(y >= 16){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-128), 0, 30)+OFFLEFT, 0); } else if(y >= 12){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-96), 0, 30)+OFFLEFT, 0); } else if(y >= 8){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-64), 0, 30)+OFFLEFT, 0); } else if(y >= 4){ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)-32), 0, 30)+OFFLEFT, 0); } else{ tv.draw_row(offset+12+y, OFFLEFT, constrain(((prevol[inp]>>2)), 0, 30)+OFFLEFT, 0); } } //write to pot.. if((inp == 0) || (inp == 1)){ //tv.print(prevol[inp]>>3, DEC); write_dac(inp, 1023-vol[inp+pss[inp]][aposition[inp]]); } } } //lengte van de sequence inline void updateLength(byte inp, byte offset){ //clear tv.draw_rect(0, 40+offset, OFFLEFT, 2, 0, 0); if(!displayMode){//display sequnecer edit length //draw new tv.draw_rect(0, 40+offset, (sizer(length[inp+pss[inp]])+2), 2, 1, 1); } else{//display master sequnecer playback length //draw new tv.draw_rect(0, 40+offset, (sizer(mlength[inp])+2), 2, 1, 1); } } //speed van de sequence inline void updateSpeed(){ //clear tv.draw_rect(0, 91, OFFLEFT, 1, 0, 0); //draw new tv.draw_rect(0, 91, (sizer(bpmspeed)+2), 1, 1, 1); } inline void updatePattern(byte inp){ //draw patern select menu //clear tv.draw_rect(OFFLEFT+30, off[inp]+1, 1, 30, 0, 0); //new tv.draw_rect(OFFLEFT+30, off[inp]+((tpss[inp]>>1)*8)+1, 1, 6, 1, 1); } inline void updatePatternEdit(byte x, byte inp){ //draw patern in sequence //clear old tv.draw_rect(sizer(x), off[inp]+1, 2, 31, 0, 0); //draw new tv.draw_rect(sizer(x), off[inp]+((mpatern[inp][x]>>1)*8)+1, 2, 6, 1, 1); } //buttons enzo.. inline void updateSelect(){ //save only when you realy want to if(!SAVER){ if(!SPEER){ if(recTimer < 21){ recTimer++; //clear old tv.draw_rect(OFFLEFT+1, 91, 21, 1, 1, 1); //draw new tv.draw_rect(OFFLEFT+1, 91, recTimer, 1, 0, 0); }else if(recTimer == 21){ recTimer++; store(); while(!SPEER){ } } }else if(!LENGR){ if(recTimer < 21){ recTimer++; //clear old tv.draw_rect(OFFLEFT+1, 91, 21, 1, 0, 0); //draw new tv.draw_rect(OFFLEFT+1, 91, recTimer, 1, 1, 1); }else if(recTimer == 21){ recTimer++; load(); while(!LENGR){ } } } if(!SPEER && !LENGR){ //clear old tv.draw_rect(OFFLEFT+1, 91, 22, 1, 0, 0); recTimer = 0; } }else{ //clear old tv.draw_rect(OFFLEFT+1, 91, 22, 1, 0, 0); recTimer = 0; } if(!MODER){ //change mode if(!butDelay){ butDelay = 16; displayMode = !displayMode; updateLength(0, off[0]); updateLength(1, off[1]); } } else if(!SPEER){ prevBut = 1; //set speed if(!FORWR && !butDelay){ if(bpmspeed < 15){ bpmspeed++; } }else if(!BACKR && !butDelay){ if(bpmspeed > 0){ bpmspeed--; } }else if(!LENGR){ external = !external; if(external){ //external sync //clear old tv.draw_rect(OFFLEFT+24, 91, 3, 1, 0, 0); //draw new tv.draw_rect(OFFLEFT+24, 91, 3, 1, 1, 1); }else{ //clear old tv.draw_rect(OFFLEFT+24, 91, 3, 1, 1, 1); //draw new tv.draw_rect(OFFLEFT+24, 91, 3, 1, 0, 0); } } else if(!VOLUR){ //mute channel if(!butDelay){ butDelay = 8; mute[modSelectY] = !mute[modSelectY]; } if(mute[modSelectY]){ //clear old tv.draw_rect(OFFLEFT+1, off[modSelectY]+1, 26, 2, 0, 0); //draw new tv.draw_rect(OFFLEFT+1, off[modSelectY]+1, 26, 2, 1, 1); } else{ //clear old tv.draw_rect(OFFLEFT+1, off[modSelectY]+1, 26, 2, 1, 1); //draw new tv.draw_rect(OFFLEFT+1, off[modSelectY]+1, 26, 2, 0, 0); } } else if(!COPYR){ //select patern if(!butDelay){ butDelay = 8; if(!displayMode){ tpss[modSelectY] += 2; if(tpss[modSelectY] > 7){ tpss[modSelectY] = 0; } updatePattern(modSelectY); } else{ mpatern[modSelectY][modSelectX] += 2; if(mpatern[modSelectY][modSelectX] > 7){ mpatern[modSelectY][modSelectX] = 0; } updatePatternEdit(modSelectX, modSelectY); } } } while(!LENGR){ } updateSpeed(); } else if(!LENGR){ prevBut = 0; //set length if(!FORWR && !butDelay){ if(!displayMode){ //pattern mode if(length[modSelectY+pss[modSelectY]] < 15){ length[modSelectY+pss[modSelectY]]++; } } else{ //song mode if(mlength[modSelectY] < 15){ mlength[modSelectY]++; } } } else if(!BACKR && !butDelay){ if(!displayMode){ //pattern if(length[modSelectY+pss[modSelectY]] > 0){ length[modSelectY+pss[modSelectY]]--; } } else{ //song mode if(mlength[modSelectY] > 0){ mlength[modSelectY]--; } } } else if(!VOLUR){ //reset selected if(!displayMode){ resetpos[modSelectY] = true; updatePosistionCursor(modSelectY, off[modSelectY]); } else{ resetpos[modSelectY] = true; mresetpos[modSelectY] = true; updatePosistionCursor(modSelectY, off[modSelectY]); } } else if(!COPYR){ //reset all if(!displayMode){ resetpos[0] = true; resetpos[1] = true; updatePosistionCursor(0, off[0]); updatePosistionCursor(1, off[1]); } else{ resetpos[0] = true; resetpos[1] = true; mresetpos[0] = true; mresetpos[1] = true; updatePosistionCursor(0, off[0]); updatePosistionCursor(1, off[1]); } } updateLength(modSelectY, off[modSelectY]); } else if(!COPYR && !displayMode){//only copy when in pattern mode, not in song mode if((prevBut != 3) && (prevBut != 2)){ pvs = vol[modSelectY+pss[modSelectY]][modSelectX]; } prevBut = 3; if(!FORWR && !butDelay){ modSelectX++; if(modSelectX > 15){ modSelectX = 0; modSelectY++; if(modSelectY > 1){ modSelectY = 0; } } } if(!BACKR && !butDelay){ modSelectX--; if(modSelectX < 0){ modSelectX = 15; modSelectY--; if(modSelectY < 0){ modSelectY = 1; } } } vol[modSelectY+pss[modSelectY]][modSelectX] = pvs; //clear old volume at position tv.draw_rect(sizer(modSelectX), off[modSelectY], 2, 31, 1, 1); //draw new volume at positioarn tv.draw_rect(sizer(modSelectX), off[modSelectY], 2, min(vol[modSelectY+pss[modSelectY]][modSelectX]>>5, 30), 0, 0); } else if(!VOLUR){ static int volcount = 0; pvs = vol[modSelectY+pss[modSelectY]][modSelectX]; prevBut = 2; //set volume if(!BACKR){ volcount++; if(vol[modSelectY+pss[modSelectY]][modSelectX] < 1023){ vol[modSelectY+pss[modSelectY]][modSelectX] = min(vol[modSelectY+pss[modSelectY]][modSelectX] + volcount, 1023); } } else if(!FORWR){ volcount++; if(vol[modSelectY+pss[modSelectY]][modSelectX] > 0){ vol[modSelectY+pss[modSelectY]][modSelectX] = max(vol[modSelectY+pss[modSelectY]][modSelectX] - volcount, 0); } } else{ volcount = 0; } volcount = min(volcount, 32); if(!displayMode){ //clear old volume at position tv.draw_rect(sizer(modSelectX), off[modSelectY], 2, 31, 1, 1); //draw new volume at position tv.draw_rect(sizer(modSelectX), off[modSelectY], 2, min(vol[modSelectY+pss[modSelectY]][modSelectX]>>5, 30), 0, 0); } else{ //update pattern instead of volume } } else{ //move cursor if(!FORWR && !butDelay){ modSelectX++; if(modSelectX > 15){ modSelectX = 0; modSelectY++; if(modSelectY > 1){ modSelectY = 0; } } } if(!BACKR && !butDelay){ modSelectX--; if(modSelectX < 0){ modSelectX = 15; modSelectY--; if(modSelectY < 0){ modSelectY = 1; } } } } if(!FORWR || !BACKR){ curani = false; curflip = true; curcount = 0; if(!butDelay && (butCount < 6)){ butCount++; butDelay = 3; } } else{ curani = true; butCount = 0; } if(butDelay){ butDelay--; } //clear old select tv.draw_rect(0, off[0]+33, OFFLEFT, 2, 0, 0); tv.draw_rect(0, off[1]+33, OFFLEFT, 2, 0, 0); //draw new curcount++; if(curcount > 8){ curcount = 0; curflip = !curflip; } if(!curani){ tv.draw_rect(sizer(modSelectX), off[modSelectY]+33, 2, 2, 1, 1); } else{ if(!curflip){ tv.draw_rect(sizer(modSelectX), off[modSelectY]+33, 2, 2, 0, 0); } else{ tv.draw_rect(sizer(modSelectX), off[modSelectY]+33, 2, 2, 1, 1); } } } void store(){ int address = 0; //schrijf voor de zekerheid een var hier. EEPROM.write(address, 1); address++; for(byte i=0; i<8; i++){ for(byte j=0; j<16; j++){ //read interegers from EEPROM byte lowB = ((vol[i][j] >> 0) & 0xFF); byte highB = ((vol[i][j] >> 8) & 0xFF); EEPROM.write(address, lowB); address++; EEPROM.write(address, highB); address++; } } for(byte i=0; i<2; i++){ for(byte j=0; j<16; j++){ EEPROM.write(address, mpatern[i][j]); address++; } } for(byte i=0; i<2; i++){ EEPROM.write(address, prevol[i]); address++; } for(byte i=0; i<8; i++){ EEPROM.write(address, length[i]); address++; } for(byte i=0; i<2; i++){ EEPROM.write(address, mlength[i]); address++; } for(byte i=0; i<2; i++){ EEPROM.write(address, aposition[i]); address++; } for(byte i=0; i<2; i++){ EEPROM.write(address, mposition[i]); address++; } EEPROM.write(address, bpmspeed); address++; EEPROM.write(address, external); } void load(){ int address = 0; if(EEPROM.read(0) == 1){ address++; for(byte i=0; i<8; i++){ for(byte j=0; j<16; j++){ //read interegers from EEPROM byte lowB = EEPROM.read(address); address++; byte highB = EEPROM.read(address); address++; vol[i][j] = ((lowB << 0) & 0xFF) + ((highB << 8) & 0xFF00); } } for(byte i=0; i<2; i++){ for(byte j=0; j<16; j++){ mpatern[i][j] = EEPROM.read(address); address++; } } for(byte i=0; i<2; i++){ prevol[i] = EEPROM.read(address); address++; } for(byte i=0; i<8; i++){ length[i] = constrain(EEPROM.read(address), 0, 15); address++; } for(byte i=0; i<2; i++){ mlength[i] = constrain(EEPROM.read(address), 0, 15); address++; } for(byte i=0; i<2; i++){ aposition[i] = constrain(EEPROM.read(address), 0, 15); address++; } for(byte i=0; i<2; i++){ mposition[i] = constrain(EEPROM.read(address), 0, 15); address++; } bpmspeed = constrain(EEPROM.read(address), 1, 15); address++; external = constrain(EEPROM.read(address), 0, 1); updateSpeed(); //tv.draw_row(OFFLEFT, 0, 128, 0); //fix for bug if cant seem to find fix for } } //from here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238548844 //for use with mcp4902 //void write_dac(boolean ab, int sample) //{ // // splits int sample in to two bytes // byte dacSPI0 = 0; // byte dacSPI1 = 0; // dacSPI0 = (sample >> 4) & 0x00FF; // move first half if the bits to make space for the config bits // if(ab){ // dacSPI0 |= (1 << 7); // A/B: DACa or DACb - Forces 7th bit of x to be 1. all other bits left alone. // } // dacSPI0 |= 0x30; // BUF is low, GA bit is 1, and SHDN is 1. // dacSPI1 = (sample<<4) & 0x00FF; // move bits to teh left for 4 null bits at the end.. // digitalWrite(SLAVESELECT,LOW); // SPDR = dacSPI0; // Start the transmission // while (!(SPSR & (1<> 8) & 0x00FF; //byte0 = takes bit 15 - 12 if(ab){ dacSPI0 |= (1 << 7); // A/B: DACa or DACb - Forces 7th bit of x to be 1. all other bits left alone. } dacSPI0 |= 0x30; // BUF is low, GA bit is 1, and SHDN is 1. dacSPI1 = sample & 0x00FF; //byte1 = takes bit 11 - 0 digitalWrite(SLAVESELECT,LOW); SPDR = dacSPI0; // Start the transmission while (!(SPSR & (1< 1){ bpmspeed--; } } } } //////// draw things // draw (when not erase) if(LENGR){ tv.set_pixel(pss[0], pss[1], 0); //clear old } //move cursor if(SPEER || !SPEER && !LENGR){ if(!butDelay){ butDelay = 3; if(!VOLUR){ pss[1] = constrain(pss[1], 1, 92); pss[1]++; } if(!COPYR){ pss[1] = constrain(pss[1], 1, 92); pss[1]--; } if(!BACKR){ pss[0] = constrain(pss[0], 1, 92); pss[0]--; } if(!FORWR){ pss[0] = constrain(pss[0], 1, 92); pss[0]++; } } } else{ butCount = 6; } //draw cursor if(SPEER){ tv.set_pixel(pss[0], pss[1], 1); //write new } //erase part if(!SPEER && !LENGR){ tv.draw_row(pss[1], 0, 96, 0); } //////// move the ball //interval for speed of sequence curcount++; if(curcount >= 16-bpmspeed){ curcount = 0; //draw when trigger is comming in if(ACTIV){ //clear old position tv.set_pixel(curpos[0], curpos[1], 0); } else{ tv.set_pixel(curpos[0], curpos[1], 0); //move dot when beat tetected dposition++; if(dposition > 3){ dposition = 0; } switch (dposition){ case 0: curpos[0]++; break; case 1: curpos[1]++; break; case 2: curpos[0]--; break; case 3: curpos[1]--; break; } curpos[0] = constrain(curpos[0], 0, 95); curpos[1] = constrain(curpos[1], 0, 93); } boolean found = false; //collision detection if(tv.get_pixel(curpos[0]+1, curpos[1]) || curpos[0] >= 95){ xypos[0] = -1; found = true; } if(tv.get_pixel(curpos[0], curpos[1]+1) || curpos[1] >= 93){ xypos[1] = -1; found = true; } if(tv.get_pixel(curpos[0]-1, curpos[1]) || curpos[0] <= 0){ xypos[0] = +1; found = true; } if(tv.get_pixel(curpos[0], curpos[1]-1) || curpos[1] <= 0){ xypos[1] = +1; found = true; } if(!found){ if(tv.get_pixel(curpos[0]-1, curpos[1]-1)){ xypos[0] = +1; xypos[1] = +1; } if(tv.get_pixel(curpos[0]+1, curpos[1]-1)){ xypos[0] = -1; xypos[1] = +1; } if(tv.get_pixel(curpos[0]-1, curpos[1]+1)){ xypos[0] = +1; xypos[1] = -1; } if(tv.get_pixel(curpos[0]+1, curpos[1]+1)){ xypos[0] = -1; xypos[1] = -1; } } //move to new position curpos[0] += xypos[0]; curpos[1] += xypos[1]; //draw new position tv.set_pixel(curpos[0], curpos[1], 2); write_dac(0, curpos[0]<<3); write_dac(1, 93-curpos[1]<<3); } butDelay = max(1, butDelay); butDelay--; tv.delay(1); }