// Jerry Chiang // Anderson Lin // 04/13/05 // ECE 476, Final Project: Vocal Trainer #include #include #include // for sine //I like these definitions #define begin { #define end } #define countMS 62 //ticks/mSec #define maxkeys 16 // max number of keys on a keypad #define enter 12 // the enter button is the pound key #define DELAYDONE 3 // DELAYDONE*30ms = lockout time between terminating inputs #define KEYSIZE 9 // size of keystr array #define MaxInput 128 // size of music input array // size of each of the demo songs #define Music0Size 27 #define Music1Size 63 #define Music2Size 68 #define Music3Size 90 #define Music4Size 70 #define Music5Size 67 #define Music6Size 94 #define Music7Size 54 #define Music8Size 71 #define Music9Size 110 // LCD #define LCDwidth 16 //characters #asm .equ __lcd_port=0x15 #endasm #include // LCD driver routines #include // sprintf #include // atoi #define MainMenuSize 111 #define MusicMenuSize 193 #define SetMenuSize 139 // subroutines void initialize(void); // all the usual mcu stuff void initkey(void); // sets keypad up for next string void keypad(void); // scans keypad void debounce(void); // keypad debounce state machine void formatchecker(void); // makes sure format is proper for non-menu entries void noterep(unsigned char pitchmatch); // find the pitch formatted as a string void RunKeypad(void); // runs the keypad, enabled by interrupt unsigned char findpitch(float freq); // find the closest pitch unsigned char nextstate, mainstate; // program states unsigned char DoKeypad, keyB, keyD, butnumB, butnumD, butnum, maybe, done, donecount, startover, FormatOK; unsigned char Init3p, Init3d, InitL, InitP, PlayFromFlag, ListenFromFlag, setfrom, FromListen; int anipos; unsigned char keycount, keyready, TerminateType; unsigned char menu_selection, ValidMenuSelection, AlreadyGotSelection, StateOneFlag, STOP; char keystr[KEYSIZE]; char tmpkeystr[2]; //modified key pad scan table flash unsigned char keytbl[16]={0xee, 0xed, 0xeb, 0xef, 0xde, 0xdd, 0xdb, 0xdf, 0xbe, 0xbd, 0xbb, 0xbf, 0xfe, 0xfd, 0xfb, 0x7f}; flash unsigned char keypd[33]={'1','4','7','*','2','5','8','0','3','6','9','#','Q','R','S','T', 'A','B','C','D','E','F','G','A','C','D','F','G','W','X','Y','Z','#'}; // enumerated states enum {START=1, DEBOUNCE, TERMINATOR, PRESSED, RELEASE, DONE}; enum {WELCOME=1, WELCOME_WAIT, MENU_INIT, MENU_UPDATE, MENU_WAIT, ONE_INIT, TWO_INIT, THREE_INIT, THREE_INIT2, FOUR_INIT, FIVE_INIT, SIX_INIT, DEMO_WAIT, DEMO_UPDATE, ONE, TWO, THREE, THREE_P, THREE_D, LISTEN_INIT, LISTEN, SCROLLING_UPDATE, FOUR, FIVE, SIX, PLAY_INIT, PLAY, SETMENU_WAIT, SETMENU_UPDATE, SHOW_SETTINGS, SET_INIT, SETBEAT, SETGRACE, SETSCORE, SETTUNER, CUSTOMTUNER, SETINPUT, SETGENDER, SETLISTENMODE, SETSAVE, DOCUMENTATION, CONFIRMATION}; // generic variables unsigned int i, t, somet; unsigned char j; long time; unsigned char LetterIndex, NumberIndex; // indexes into pitch table char graceOn, scoreOn, playinputOn, SaveInput, Male, FFTMode; // customizeable flags unsigned int bpm, modbpm, tunerfreq, bpmfactor; // customizeable variables int sumdur; // duration value char tmpdur, tmpdur1, tmpdur2, endofchar, tmpdur1Valid, tmpdur2Valid; // format checker variables int ptr; // pointer for input array unsigned char input[MaxInput][2]; // buffer to hold current input unsigned char inputstore[MaxInput][2]; // where to put in memory? // buffer to hold saved input char lcd_buffer[17]; // LCD display buffer char nullchar[1] = ""; // formatchecker stuff char nNumber, nLetter, inRange; int tmpnum; // listen countdown float countdown; char countdownEN, ListenLoopDone; // For Music Play & Listen unsigned long accumulator @0x700; unsigned char highbyte @0x703; //the HIGH byte of the accumulator variable unsigned long increment, total_length, lengthtmp, next; unsigned char sineTable1[256] @0x710; //need loc to avoid glitch unsigned char length, sd_shift; unsigned char FreqUpdate23, FreqUpdate45; // boolean flags unsigned int x, aheadx, timep, numNotes, toggledot, togglecount, delta_t; unsigned int smallest_duration; char ListenInit1Flag; float pitch; double resolution; char count; char MainPlayLoopDone, SubPlayLoopDone, SubPlayLoopPrep; char screen[16]; char oneChar[2]; unsigned char sharpOn; flash char MainMenuTitle[MainMenuSize] = "1. Play Music 2. Karaoke Mode 3. Custom Mode 4. Tuner Mode 5. Easy Match Mode 6. Modify Settings "; flash char MusicMenuTitle[MusicMenuSize] = "0. Happy Birthday 1. Take Me Out To The Ballgame 2. Voiceka 3. Rainbow 4. Small World 5. Power of Love 6. Beauty & the Beast 7. Music of the Night 8. Close Every Door 9. Caro mio ben "; flash char SetMenuTitle[SetMenuSize] = "1. Set Beat 2. Grace Period 3. Show Score 4. Set Tuner 5. Play Input 6. Gender 7. Calc Mode 8. Autosave 9. Documentation "; // Happy Birthday to You flash unsigned char music0[Music0Size][2] = {{0x2a,6},{0x2a,2},{0x30,8},{0x2a,8},{0x33,8},{0x32,16}, {0x2a,6},{0x2a,2},{0x30,8},{0x2a,8},{0x35,8},{0x33,16}, {0x2a,6},{0x2a,2},{0x3a,8},{0x37,8},{0x33,8},{0x32,8},{0x30,8},{0x00,8}, {0x38,6},{0x38,2},{0x37,8},{0x33,8},{0x35,8},{0x33,16},{0x5b,8}}; // Take me out to the Ballgame flash unsigned char music1[Music1Size][2] = {{0x23,8},{0x33,4},{0x30,4},{0x2a,4},{0x27,4},{0x2a,12},{0x25,12}, {0x23,8},{0x33,4},{0x30,4},{0x2a,4},{0x27,4},{0x2a,24}, {0x30,4},{0x2b,4},{0x30,4},{0x27,4},{0x28,4},{0x2a,4},{0x30,8},{0x28,4},{0x25,12}, {0x30,8},{0x30,4},{0x30,4},{0x32,4},{0x33,4},{0x35,4},{0x32,4},{0x30,4},{0x2a,4}, {0x27,4},{0x25,4},{0x23,8},{0x33,4},{0x30,4},{0x2a,4},{0x27,4},{0x2a,12},{0x25,8}, {0x25,4},{0x23,8},{0x25,4},{0x27,4},{0x28,4},{0x2a,4},{0x30,16}, {0x30,4},{0x32,4},{0x33,8},{0x00,4},{0x33,8},{0x00,4},{0x33,4},{0x32,4},{0x30,4},{0x2a,4}, {0x29,4},{0x2a,4},{0x30,12},{0x32,12},{0x33,24},{0x5b,8}}; // L'Autumn Voiceka flash unsigned char music2[Music2Size][2] = {{0x23,6},{0x23,2},{0x23,4},{0x26,8},{0x23,4},{0x21,6},{0x21,2},{0x21,4},{0x26,12}, {0x1b,8},{0x21,4},{0x23,8},{0x1b,4},{0x1a,6},{0x1a,2},{0x1a,4},{0x23,12}, {0x18,6},{0x18,2},{0x18,4},{0x1b,8},{0x18,4},{0x16,6},{0x16,2},{0x16,4},{0x1b,12}, {0x18,6},{0x18,2},{0x18,4},{0x1b,8},{0x23,4},{0x26,1},{0x24,8},{0x23,4},{0x21,12}, {0x28,6},{0x28,2},{0x28,4},{0x2b,8},{0x28,4},{0x26,8},{0x23,4},{0x2b,12}, {0x24,6},{0x24,2},{0x24,4},{0x28,8},{0x24,4},{0x23,12},{0x21,12}, {0x23,6},{0x23,2},{0x23,4},{0x26,8},{0x23,4},{0x21,6},{0x21,2},{0x21,4},{0x26,12}, {0x1b,8},{0x21,4},{0x23,8},{0x24,2},{0x23,2},{0x21,8},{0x1a,4},{0x1b,12},{0x5b,8}}; // Rainbow flash unsigned char music3[Music3Size][2] = {{0x28,16},{0x28,16},{0x28,4},{0x28,4},{0x26,4},{0x24,4},{0x23,4},{0x28,4},{0x2b,4}, {0x2b,16},{0x26,20},{0x00,16},{0x26,16},{0x26,16},{0x26,4},{0x26,4},{0x24,4},{0x23,4}, {0x21,4},{0x24,4},{0x28,4},{0x28,16},{0x23,20},{0x21,4},{0x21,4},{0x23,4},{0x24,4}, {0x00,8},{0x28,12},{0x26,8},{0x26,16}, {0x28,16},{0x28,16},{0x28,4},{0x28,4},{0x26,4},{0x24,4},{0x23,4},{0x28,4},{0x2b,4}, {0x2b,16},{0x26,20},{0x00,16},{0x26,16},{0x26,16},{0x26,4},{0x26,4},{0x24,4},{0x23,4}, {0x21,4},{0x24,4},{0x28,4},{0x28,16},{0x23,20},{0x21,4},{0x21,4},{0x23,4},{0x24,4}, {0x00,8},{0x28,12},{0x26,8},{0x26,40}, {0x26,4},{0x34,4},{0x33,4},{0x31,4},{0x00,2},{0x33,2},{0x2b,20}, {0x26,4},{0x34,4},{0x33,4},{0x31,4},{0x00,2},{0x33,2},{0x2b,20}, {0x26,4},{0x34,4},{0x33,4},{0x31,6},{0x33,2},{0x38,32},{0x36,4},{0x36,16}, {0x34,4},{0x33,4},{0x31,4},{0x33,4},{0x2b,32},{0x5b,8}}; // It's a Small World flash unsigned char music4[Music4Size][2] = {{0x27,4},{0x28,4},{0x2a,8},{0x37,8},{0x33,8}, {0x35,4},{0x33,4},{0x33,8},{0x32,8},{0x32,8}, {0x25,4},{0x27,4},{0x28,8},{0x35,8},{0x32,8}, {0x33,4},{0x32,4},{0x30,8},{0x2a,8},{0x2a,8}, {0x27,4},{0x28,4},{0x2a,8},{0x33,4},{0x35,4},{0x37,8}, {0x35,4},{0x33,4},{0x30,8},{0x35,4},{0x37,4},{0x38,8}, {0x37,4},{0x35,4},{0x2a,8},{0x38,8},{0x37,8},{0x35,8},{0x33,18},{0x00,10}, {0x33,12},{0x33,4},{0x37,8},{0x33,8},{0x35,12},{0x35,4},{0x35,12},{0x00,4}, {0x35,12},{0x35,4},{0x38,8},{0x35,8},{0x37,12},{0x37,4},{0x37,12},{0x00,4}, {0x37,12},{0x37,4},{0x3a,8},{0x37,8},{0x38,12},{0x38,4},{0x38,8}, {0x37,4},{0x35,4},{0x2a,16},{0x32,16},{0x33,18},{0x00,6},{0x5b,8}}; // The Power of Love flash unsigned char music5[Music5Size][2] = {{0x2a,4},{0x33,4},{0x35,8},{0x38,8},{0x37,20},{0x00,4}, {0x33,4},{0x33,4},{0x35,4},{0x37,4},{0x33,6},{0x32,2},{0x30,16},{0x00,8}, {0x33,4},{0x33,4},{0x35,4},{0x37,4},{0x38,8},{0x30,4},{0x30,16},{0x00,8}, {0x33,2},{0x33,2},{0x33,4},{0x35,4},{0x37,4},{0x37,6},{0x35,16},{0x00,8}, {0x2a,4},{0x3a,6},{0x37,4},{0x35,4},{0x37,4},{0x35,2},{0x33,16},{0x00,8}, {0x33,2},{0x33,2},{0x33,4},{0x35,4},{0x37,4},{0x33,6},{0x32,2},{0x30,16},{0x00,8}, {0x33,4},{0x33,4},{0x35,4},{0x37,4},{0x38,6},{0x00,2}, {0x38,4},{0x38,4},{0x38,4},{0x38,4},{0x37,4},{0x37,4},{0x35,12}, {0x00,4}, {0x2a,4},{0x33,8},{0x33,4},{0x33,12},{0x00,6},{0x5b,8}}; // Beauty and the Beast flash unsigned char music6[Music6Size][2] = {{0x27,4},{0x2a,4},{0x32,4},{0x33,4},{0x28,12},{0x00,12}, {0x27,6},{0x2a,6},{0x32,2},{0x33,2},{0x35,12},{0x00,12}, {0x33,4},{0x35,4},{0x37,4},{0x38,4},{0x3a,12},{0x00,4}, {0x3a,6},{0x38,6},{0x37,2},{0x35,2},{0x33,12},{0x00,4}, {0x38,6},{0x37,4},{0x35,2},{0x33,4},{0x32,1},{0x30,1},{0x2a,8},{0x00,12}, {0x27,6},{0x2a,6},{0x32,2},{0x33,2},{0x28,12},{0x00,12}, {0x27,6},{0x2a,6},{0x33,2},{0x35,2},{0x35,12},{0x00,12}, {0x37,4},{0x35,4},{0x37,4},{0x3a,4},{0x33,12},{0x00,4}, {0x33,4},{0x32,4},{0x33,4},{0x37,4},{0x28,12},{0x00,4}, {0x37,4},{0x38,4},{0x35,6},{0x37,6},{0x33,12},{0x00,12}, {0x33,4},{0x35,4},{0x37,4},{0x38,4},{0x3a,12},{0x00,12}, {0x33,4},{0x35,4},{0x37,4},{0x38,4},{0x40,6},{0x3a,12},{0x00,12}, {0x33,4},{0x35,4},{0x37,4},{0x38,4},{0x3a,12},{0x00,4}, {0x3a,6},{0x38,6},{0x37,2},{0x35,2},{0x33,12},{0x00,4}, {0x33,4},{0x35,4},{0x37,8},{0x33,6},{0x35,12},{0x00,12},{0x5b,8}}; // Music of the Night flash unsigned char music7[Music7Size][2] = {{0x28,8},{0x1b,8},{0x26,8},{0x1b,8},{0x24,4},{0x26,4},{0x28,4},{0x29,4},{0x26,8},{0x2b,8}, {0x28,8},{0x1b,8},{0x26,8},{0x1b,8},{0x24,4},{0x26,4},{0x28,4},{0x29,4},{0x26,8},{0x2b,8}, {0x31,4},{0x34,4},{0x34,4},{0x34,4},{0x36,8},{0x34,4},{0x33,4},{0x31,4},{0x34,4},{0x34,4}, {0x34,4},{0x36,8},{0x34,4},{0x33,4},{0x31,4},{0x34,4},{0x34,4},{0x34,4},{0x36,4},{0x34,4}, {0x31,4},{0x28,4},{0x2b,16},{0x28,4},{0x28,4},{0x26,4},{0x26,4},{0x28,4},{0x29,4},{0x2b,4}, {0x28,4},{0x26,4},{0x24,16},{0x5b,8}}; // Close Every Door flash unsigned char music8[Music8Size][2] = {{0x23,8},{0x2b,8},{0x28,8},{0x2a,8},{0x24,8},{0x23,8}, {0x23,8},{0x2b,8},{0x28,8},{0x2a,8},{0x24,8},{0x23,8}, {0x23,8},{0x28,8},{0x2b,8},{0x33,8},{0x31,12},{0x2b,4}, {0x2a,8},{0x2b,8},{0x28,8},{0x2a,24}, {0x23,8},{0x2b,8},{0x28,8},{0x2a,8},{0x24,8},{0x23,8}, {0x23,8},{0x2b,8},{0x28,8},{0x2a,8},{0x24,8},{0x23,8}, {0x23,8},{0x28,8},{0x2b,8},{0x33,8},{0x31,12},{0x2b,4}, {0x2a,8},{0x2b,8},{0x28,8},{0x33,24}, {0x33,4},{0x33,4},{0x33,8},{0x34,8},{0x36,8},{0x38,16}, {0x34,4},{0x33,4},{0x31,8},{0x33,8},{0x34,8},{0x36,24}, {0x2b,8},{0x31,8},{0x2b,8},{0x31,8},{0x33,4},{0x2b,4}, {0x28,16},{0x23,4},{0x23,4},{0x24,4},{0x2b,4},{0x28,8}, {0x27,8},{0x28,24},{0x5b,8}}; // Caro mio ben flash unsigned char music9[Music9Size][2] = {{0x33,8},{0x32,6},{0x30,2},{0x2a,16},{0x30,8},{0x2a,6},{0x28,2},{0x27,16}, {0x28,8},{0x27,4},{0x25,4},{0x2a,16},{0x23,8},{0x22,4},{0x23,4},{0x27,8},{0x25,8},{0x00,8}, {0x30,8},{0x2a,6},{0x28,2},{0x27,16},{0x28,8},{0x27,4},{0x25,4},{0x2a,4},{0x33,4}, {0x23,4},{0x28,4},{0x27,8},{0x25,6},{0x23,2},{0x23,16},{0x00,16}, {0x2a,8},{0x30,4},{0x32,4},{0x30,16},{0x30,8},{0x32,4},{0x33,4},{0x32,16}, {0x35,8},{0x33,4},{0x32,4},{0x30,4},{0x29,4},{0x2a,4},{0x33,4},{0x32,8},{0x30,6},{0x2a,2},{0x2a,16}, {0x30,8},{0x2a,6},{0x28,2},{0x27,8},{0x00,8}, {0x2a,8},{0x28,4},{0x27,4},{0x27,8},{0x25,8},{0x33,8},{0x29,4},{0x29,4},{0x2a,16}, {0x33,8},{0x32,6},{0x30,2},{0x2a,16},{0x30,8},{0x2a,6},{0x28,2},{0x27,16}, {0x28,8},{0x27,4},{0x25,4},{0x2a,4},{0x33,4},{0x23,4},{0x28,4},{0x27,8},{0x25,6},{0x23,2},{0x23,16}, {0x28,1},{0x2a,1},{0x30,8},{0x2a,6},{0x28,2},{0x27,16}, {0x28,1},{0x2a,1},{0x30,8},{0x2a,6},{0x28,2},{0x27,8},{0x00,8}, {0x33,8},{0x32,4},{0x30,4},{0x2a,16},{0x33,8},{0x00,8}, {0x27,16},{0x25,12},{0x23,4},{0x23,16},{0x5b,8}}; // pitch table float ptable[6][12] = {{0, 29.14, 30.87, 32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.00, 51.91}, {55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83}, {110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65}, {220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30}, {440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61}, {880.00, 932.33, 987.77, 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, -1}}; // scoring float freq; long deduct; unsigned long penalty; int diff; // time and cycle counting unsigned long time1, timetmp, timeL; char sample, cycle, cycle_old, cycletmp; // find note variables unsigned char pitchmatch, scale; char note[2]; interrupt [TIM0_OVF] void sgen(void) begin //the DDR code and scaling accumulator = accumulator + increment ; OCR0 = sineTable1[highbyte]; //generate rising amplitude // 62 counts is about 1 mSec count--; if (0 == count ) begin count=countMS; timep++; //in mSec end end //timer 2 compare ISR for Schmidt Trigger Algorithm interrupt [TIM2_COMP] void timer2_compare(void) begin time1++; if (somet < 64) somet++; else begin somet = 0; timeL++; // in mSec end end //INT1 for Schmidt Trigger Algorithm interrupt [EXT_INT1] void Int1(void) begin if (sample == 0) // start next sample begin time1 = 0; sample = 1; end else if (sample > 0) // getting new sample begin cycle_old = cycle; cycle++; end end //timer 0 compare ISR interrupt [TIM0_COMP] void timer0_compare(void) begin time++; if (donecount == DELAYDONE) // startover takes at least 3x30ms initkey(); if (t < 30) t++; else begin t = 0; DoKeypad = 1; // run keypad end end void main(void) begin initialize(); //endless loop to read keyboard while(1) begin switch(mainstate) begin case WELCOME: // Welcome State TerminateType = 0; lcd_clear(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("WELCOME!"); //another string from flash mainstate = WELCOME_WAIT; time = 0; break; case WELCOME_WAIT: // wait 1 second TerminateType = 0; if (time < 1000) {} else mainstate = MENU_INIT; break; case MENU_INIT: // Menu Init State /* Menu 1. Play music 2. Practice with music 3. Practice with input 4. Tuner 5. Easy Match 6. Settings */ TerminateType = 0; if (DoKeypad == 1) RunKeypad(); // resets countdown init countdownEN= 0; // initialize input array for (i = 0; i < MaxInput; i++) begin input[i][0] = 0; input[i][1] = 0; end lcd_clear(); //clear the display lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("Select an option"); //string from flash anipos = 0; ValidMenuSelection = 0; AlreadyGotSelection = 0; time = 0; mainstate = MENU_WAIT; break; case MENU_WAIT: // wait 200 ms before updating scrolling menu TerminateType = 1; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); AlreadyGotSelection = 1; menu_selection = atoi(keystr); end if (time >= 200) begin mainstate = MENU_UPDATE; end break; case MENU_UPDATE: // Menu Update State // count display loop TerminateType = 1; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); time = 0; ValidMenuSelection = 0; if (AlreadyGotSelection == 0) menu_selection = atoi(keystr); if (menu_selection == 1) mainstate = ONE_INIT; else if (menu_selection == 2) mainstate = TWO_INIT; else if (menu_selection == 3) mainstate = THREE_INIT; else if (menu_selection == 4) mainstate = FOUR_INIT; else if (menu_selection == 5) mainstate = FIVE_INIT; else if (menu_selection == 6) mainstate = SIX_INIT; else // messed up... did not get right number from keystr mainstate = MENU_INIT; end else begin //scroll the text // set position anipos++; if (anipos < 5) // text not done scrolling across screen begin for (i = 0; i < 16; i++) begin lcd_buffer[i] = MainMenuTitle[i]; end lcd_gotoxy(0,1); lcd_buffer[16] = nullchar[0]; lcd_puts(lcd_buffer); end else if (anipos > MainMenuSize-1) // text is filling up the screen anipos = 0; else begin for (i = 0; i < 16; i++) begin lcd_buffer[i] = MainMenuTitle[i+anipos-5]; end lcd_buffer[16] = nullchar[0]; lcd_gotoxy(0,1); lcd_puts(lcd_buffer); end time = 0; mainstate = MENU_WAIT; end break; case ONE_INIT: // sets up option 1 TerminateType = 0; if (DoKeypad == 1) RunKeypad(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("1 Play Music"); //string from flash lcd_gotoxy(0,1); lcd_putsf("Select a song"); if (time >= 1000) begin StateOneFlag = 1; ValidMenuSelection = 0; AlreadyGotSelection = 0; time = 0; mainstate = DEMO_WAIT; end break; case TWO_INIT: // sets up option 2 TerminateType = 0; if (DoKeypad == 1) RunKeypad(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("2 Practice Music"); //string from flash lcd_gotoxy(0,1); lcd_putsf("Select a song"); if (time >= 1000) begin StateOneFlag = 0; ValidMenuSelection = 0; AlreadyGotSelection = 0; time = 0; mainstate = DEMO_WAIT; end break; case THREE_INIT: // sets up option 3 // practice with input TerminateType = 3; if (DoKeypad == 1) RunKeypad(); if (SaveInput == 1) // ask for whether or not to load saved input begin lcd_gotoxy(0,0); lcd_putsf("Load Previous"); lcd_gotoxy(0,1); lcd_putsf("Input? 1=y,2=n"); if (ValidMenuSelection == 1) begin lcd_clear(); ValidMenuSelection = 0; if (atoi(keystr) == 1) begin for (i = 0; i= 3000) begin Init3p = 1; mainstate = THREE_P; end break; case FOUR_INIT: // set up 4 TerminateType = 0; if (DoKeypad == 1) RunKeypad(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("4 Tuner"); //string from flash lcd_gotoxy(0,1); lcd_putsf(""); if (time >= 1000) mainstate = FOUR; break; case FIVE_INIT: // set up 5 TerminateType = 0; if (DoKeypad == 1) RunKeypad(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("5 Easy Match"); //string from flash lcd_gotoxy(0,1); lcd_putsf("Please sing"); if (time >= 1000) mainstate = FIVE; break; case SIX_INIT: // set up 6 TerminateType = 0; if (DoKeypad == 1) RunKeypad(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("6 Settings"); //string from flash ValidMenuSelection = 0; if (time >= 1000) begin time = 0; mainstate = SIX; end break; case DEMO_WAIT: // wait 200 ms before updating scrolling music TerminateType = 5; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); AlreadyGotSelection = 1; menu_selection = atoi(keystr); end if (time >= 200) mainstate = DEMO_UPDATE; break; case DEMO_UPDATE: // Demo Music Menu Update State // count display loop TerminateType = 5; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); time = 0; if (StateOneFlag == 1) // came from ONE_INIT mainstate = ONE; else // came from TWO_INIT mainstate = TWO; if (AlreadyGotSelection == 0) menu_selection = atoi(keystr); if (menu_selection == 0) begin for (i = 0; i < Music0Size; i++) begin input[i][0] = music0[i][0]; input[i][1] = music0[i][1]; end end else if (menu_selection == 1) begin for (i = 0; i < Music1Size; i++) begin input[i][0] = music1[i][0]; input[i][1] = music1[i][1]; end end else if (menu_selection == 2) begin for (i = 0; i < Music2Size; i++) begin if (StateOneFlag == 1 && music2[i][0] != 0x5b) input[i][0] = music2[i][0]+16; // raise my song by an octave else input[i][0] = music2[i][0]; input[i][1] = music2[i][1]; end end else if (menu_selection == 3) begin for (i = 0; i < Music3Size; i++) begin input[i][0] = music3[i][0]; input[i][1] = music3[i][1]; end end else if (menu_selection == 4) begin for (i = 0; i < Music4Size; i++) begin input[i][0] = music4[i][0]; input[i][1] = music4[i][1]; end end else if (menu_selection == 5) begin for (i = 0; i < Music5Size; i++) begin input[i][0] = music5[i][0]; input[i][1] = music5[i][1]; end end else if (menu_selection == 6) begin for (i = 0; i < Music6Size; i++) begin input[i][0] = music6[i][0]; input[i][1] = music6[i][1]; end end else if (menu_selection == 7) begin for (i = 0; i < Music7Size; i++) begin input[i][0] = music7[i][0]; input[i][1] = music7[i][1]; end end else if (menu_selection == 8) begin for (i = 0; i < Music8Size; i++) begin input[i][0] = music8[i][0]; input[i][1] = music8[i][1]; end end else if (menu_selection == 9) begin for (i = 0; i < Music9Size; i++) begin input[i][0] = music9[i][0]; input[i][1] = music9[i][1]; end end else begin // messed up if (StateOneFlag == 1) mainstate = ONE_INIT; else mainstate = TWO_INIT; end end else begin //scroll the text // set position anipos++; if (anipos < 5) // text not done scrolling across screen begin for (i = 0; i < 16; i++) begin lcd_buffer[i] = MusicMenuTitle[i]; end lcd_gotoxy(0,1); lcd_buffer[16] = nullchar[0]; lcd_puts(lcd_buffer); end else if (anipos > MusicMenuSize -1) // text is filling up the screen anipos = 0; else begin for (i = 0; i < 16; i++) begin lcd_buffer[i] = MusicMenuTitle[i+anipos-5]; end lcd_buffer[16] = nullchar[0]; lcd_gotoxy(0,1); lcd_puts(lcd_buffer); end time = 0; mainstate = DEMO_WAIT; end break; case ONE: // play a song // list of songs // if terminated, take in the input // play song until song ends or stop button pressed TerminateType = 0; if (DoKeypad == 1) RunKeypad(); PlayFromFlag = 1; InitP = 1; mainstate = PLAY_INIT; break; case TWO: // practice with music // list of songs // if terminated, take in the input // start scrolling notes until song ends or stop button pressed // display current beat // take in external clock and calculate frequency // display the difference // show score in the end TerminateType = 0; if (DoKeypad == 1) RunKeypad(); ListenFromFlag = 2; InitL = 1; mainstate = LISTEN_INIT; break; case THREE_P: // prompt pitch // if terminated, take in input & store it // if this input is the enter button, exit this loop TerminateType = 4; if (DoKeypad == 1) RunKeypad(); if (Init3p == 1) begin lcd_clear(); lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("ENTER PITCH"); //string from flash Init3p = 0; time = 0; end if (time < 200) begin end else begin if (done == 1) begin if (keystr[0] != 0) // make sure you can't enter 0 frequency at beginning... begin // set init for duration Init3d = 1; // store pitch input[ptr][0] = (unsigned char)(NumberIndex*16+LetterIndex); // get duration mainstate = THREE_D; end else begin input[ptr][0] = 0x5b; input[ptr][1] = 0; InitL = 1; mainstate = THREE; end end end break; case THREE_D: // prompt duration // if terminated, take in input & store it // if array is out of room, exit this loop TerminateType = 4; if (DoKeypad == 1) RunKeypad(); if (Init3d == 1) begin lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("ENTER DURATION"); // enter pitches and corresponding duration.. press enter at end to stop Init3d = 0; time = 0; end if (time < 200) begin end else begin if (done == 1) begin // reset init Init3p = 1; // store duration input[ptr][1] = sumdur; ptr++; // look ahead at next pitch... if ptr now indexes last entry in array if (ptr == MaxInput - 1) begin InitL = 1; input[ptr][0] = 0x5b; input[ptr][1] = 0; mainstate = THREE; end else mainstate = THREE_P; end end break; case THREE: // practice with input TerminateType = 0; if (SaveInput == 1) begin for (i = 0; i < MaxInput; i++) begin inputstore[i][0] = input[i][0]; inputstore[i][1] = input[i][1]; end end ListenFromFlag = 3; if (playinputOn == 1) begin PlayFromFlag = 3; InitP = 1; mainstate = PLAY_INIT; end else begin mainstate = LISTEN_INIT; end break; case LISTEN_INIT: // start of LISTEN path // accessed from options 2,3,3,4,5 // countdown & preparation TerminateType = 0; if (DoKeypad == 1) RunKeypad(); if (InitL == 1) begin lcd_clear(); lcd_gotoxy(0,0); lcd_putsf("Prepare To Sing"); InitL = 0; time = 0; countdownEN = 1; end else if (countdownEN == 1 && time <= 4000) begin countdown = (4000-time)/1000; lcd_gotoxy(0,1); //position to upper left on display sprintf(lcd_buffer,"COUNTDOWN: %i",(int)countdown); lcd_puts(lcd_buffer); ListenInit1Flag = 1; end else begin // set up keypad to read only enter button for STOP DDRD = 0x00; DDRB = 0b10111111; // output to all horizontal lines... and all vertical rows except 3,6,9,#. Read from B.6 PORTB= 0b01111111; // output 0 to all lines on output except for B3 // if first time through this section of ListenInit if (ListenInit1Flag == 1) begin timeL = 0; ListenInit1Flag = 0; lcd_clear(); end // set up for listening TIMSK = 0b10000000; TCCR0 = 0b00000001; OCR2 = 249; TCCR2 = 0b00001001; MCUCR = 0b00001100; GICR = 0b10000000; time1 = 0; sample = 0; cycle = 0; deduct = 0; total_length = 0; STOP = 0; bpmfactor = (unsigned int)(7500/bpm); // Set up scrolling notes display // Options 2 & 3 only if (ListenFromFlag != 4 && ListenFromFlag != 5) begin numNotes = 0; while (input[numNotes][0] != 0x5b) // find end character begin numNotes++; end smallest_duration = input[0][1]; // let the first entry be the smallest_duration for (x=0;x> 1; toggledot = 1; end else if (smallest_duration == 32) begin sd_shift = 5; modbpm = bpm >> 2; toggledot = 1; end togglecount = 1; // initialize toggle delta_t = bpmfactor*(unsigned int)(smallest_duration); // get the smallest duration in terms of time end x = 0; // index to go through input array ListenLoopDone = 1; // flag to initialize loop traversal if (timeL >= 700) begin timeL = 0; mainstate = LISTEN; end end break; case LISTEN: // listens to microphone input, computes frequency and displays output appropriate to mode TerminateType = 0; if (DoKeypad == 1) RunKeypad(); FromListen = 1; if (input[x][0] != 0x5b && STOP == 0) // if stop button not pressed and haven't reached end of input begin // check for stop if (PINB.6 == 0) STOP = 1; if (ListenLoopDone == 1) // loop to get next pitch from pitch table... runs once per note begin if (ListenFromFlag != 4 && ListenFromFlag != 5) begin if (ListenFromFlag == 2) pitch = ptable[(input[x][0]>>4)+!Male][input[x][0]&0x0f]; else pitch = ptable[input[x][0]>>4][input[x][0]&0x0f]; length = input[x][1]; end else if (ListenFromFlag == 4) begin pitch = tunerfreq; end end // if mode is 4 or 5, or time isn't up yet if (ListenFromFlag == 4 || ListenFromFlag == 5 || timeL<(bpmfactor*(unsigned int)(length))) begin ListenLoopDone = 0; // conditions for when to figure out enough cycles are taken in to compute frequency FreqUpdate23 = (cycle >= 10 && time1 > 6400 && cycle == cycle_old+1) && !(ListenFromFlag == 4 || ListenFromFlag == 5); FreqUpdate45 = (cycle >= 30 && time1 > 6400 && cycle == cycle_old+1) && (ListenFromFlag == 4 || ListenFromFlag == 5); // Frequency Sampling if (FreqUpdate23 || FreqUpdate45 || (pitch == 0 && time1 > 6400) && !(ListenFromFlag == 4 || ListenFromFlag == 5)) // get freq on normal mode 2,3,4,5 update and also when pitch = 0 for modes 2&3 begin timetmp = time1; cycletmp = cycle; time1 = 0; cycle = 0; cycle_old = 0; sample = 0; freq = (float)cycletmp*64000/timetmp; end diff = (int)(freq - pitch); // Frequency Display if (timeL % delta_t == 0 && ListenFromFlag != 4 && ListenFromFlag != 5) begin // update scrolling notes display mainstate = SCROLLING_UPDATE; // calculate score if (graceOn == 1 && timeL <= 300) {} else begin deduct = deduct + abs(diff); total_length++; end end else if (timeL % 750 == 0 && ListenFromFlag == 4) begin // for tuner mode, just display the pitch that you're trying to sing lcd_clear(); lcd_gotoxy(0,0); sprintf(lcd_buffer,"Sing %i Hz",tunerfreq); lcd_puts(lcd_buffer); lcd_gotoxy(0,1); sprintf(lcd_buffer,"diff = %i",diff); lcd_puts(lcd_buffer); end else if (ListenFromFlag == 5 && timeL % 250 == 0) begin // for easy match, find the closest frequency pitchmatch = findpitch(freq); scale = (pitchmatch>>4) + 1; noterep(pitchmatch); lcd_clear(); //clear the display lcd_gotoxy(0,0); if (sharpOn == 0) sprintf(lcd_buffer,"Note is %c%i",note[0],scale); else sprintf(lcd_buffer,"Note is %c%c%i",note[0],note[1],scale); lcd_puts(lcd_buffer); //another string from flash lcd_gotoxy(0,1); sprintf(lcd_buffer,"Freq = %i",(int)freq); lcd_puts(lcd_buffer); //another string from flash end end else begin // time is up ListenLoopDone = 1; timeL = 0; if (ListenFromFlag != 4) begin x++; time = 0; end end end else begin // get the old stuff back //turn on timer 0 COMPARE ISR OCR0 = 250; //set the compare to 250 time ticks TCCR0=0b00001011; //prescalar to 64 and turn on clear-on-match TIMSK = 0x02; MCUCR = 0b00000000; GICR = 0b00000000; // show score if (ListenFromFlag != 4 && ListenFromFlag !=5 && scoreOn == 1) begin if (time < 100) begin penalty = (int)(deduct/total_length); lcd_clear(); end if (time < 2000)// (done == 0) // begin sprintf(lcd_buffer,"Score = %i",penalty); lcd_gotoxy(0,0); //position to upper left on display lcd_puts(lcd_buffer); //another string from flash lcd_gotoxy(0,1); //position to upper left on display if (penalty < 10) lcd_putsf("You got an A"); //another string from flash else if (penalty < 20) lcd_putsf("You got an B"); //another string from flash else if (penalty < 30) lcd_putsf("You got an C"); //another string from flash else if (penalty < 50) lcd_putsf("You got an D"); //another string from flash else lcd_putsf("You got an F"); //another string from flash end else mainstate = MENU_INIT; end else mainstate = MENU_INIT; end break; case SCROLLING_UPDATE: // displays scrolling notes, flashing bpm, and frequency difference // flashing bpm dot if (togglecount < (toggledot << 1)) togglecount++; else togglecount = 1; // display top row lcd_gotoxy(0,0); if (FromListen == 1) begin // for listen: if (togglecount > toggledot) begin sprintf(lcd_buffer,"*** %+4i Hz %3i",diff,modbpm); lcd_puts(lcd_buffer); end else begin sprintf(lcd_buffer,"*** %+4i Hz o%3i",diff,modbpm); lcd_puts(lcd_buffer); end end else begin // for play: if (togglecount > toggledot) begin sprintf(lcd_buffer,"*** bpm: %3i",modbpm); lcd_puts(lcd_buffer); end else begin sprintf(lcd_buffer,"*** bpm: %3i",modbpm); lcd_puts(lcd_buffer); end end aheadx = x; lengthtmp = length; for (i=0; i<16;i++) begin if ((FromListen == 1 && (timeL + i*delta_t >= (bpmfactor*(unsigned int)(lengthtmp)))) || (FromListen == 0 && (timep + i*delta_t >= (bpmfactor*(unsigned int)(lengthtmp))))) begin aheadx++; if (aheadx < numNotes) // numNotes indexes the last character of the array, which is the STOP char. begin next = (unsigned long)(input[aheadx][1] >> sd_shift); lengthtmp = lengthtmp + next; // update screen[i] = input[aheadx][0]; end else screen[i] = -2; end else screen[i] = -2; end // first note is always the current note. screen[0] = input[x][0]; // the current frequency // display scrolling notes j=0; // index for LCD i=0; // index for screen while (i<16 && j<16) begin if (screen[i] != -2) begin scale = (screen[i]>>4) + 1; noterep(screen[i]); // display note[0] // if screen[i] refers you to a rest, then display zeros... if (screen[i] == 0) sprintf(oneChar,"0"); else sprintf(oneChar,"%c",note[0]); // display note lcd_buffer[j] = oneChar[0]; j++; if (j < 16) begin if (sharpOn == 1) begin sprintf(oneChar,"%c",note[1]); // display sharp lcd_buffer[j] = oneChar[0]; j++; end if (j < 16) begin // display scale // if screen[i] refers you to a rest, then display zeros... if (screen[i] == 0) sprintf(oneChar,"0"); else sprintf(oneChar,"%i",scale); lcd_buffer[j] = oneChar[0]; j++; // add an extra space after each note if (j < 16) begin sprintf(oneChar," "); lcd_buffer[j] = oneChar[0]; j++; end end end end else begin sprintf(oneChar," "); lcd_buffer[j] = oneChar[0]; j++; end i++; end lcd_buffer[16] = nullchar[0]; lcd_gotoxy(0,1); lcd_puts(lcd_buffer); if (FromListen == 1) mainstate = LISTEN; else mainstate = PLAY; break; case FOUR: // tuner TerminateType = 0; if (DoKeypad == 1) RunKeypad(); ListenFromFlag = 4; PlayFromFlag = 4; input[0][0] = 0; InitP = 1; ListenInit1Flag = 1; mainstate = PLAY_INIT; break; case FIVE: // easy match // take in external clock and get frequency // show predicted pitch and amount of difference // continue until exit button is pressed TerminateType = 0; if (DoKeypad == 1) RunKeypad(); ListenFromFlag = 5; // tells listen state to do binary search needed for easy match input[0][0] = 0; InitL = 1; mainstate = LISTEN_INIT; break; case SIX: // settings TerminateType = 3; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); if (atoi(keystr) == 1) begin time = 0; mainstate = SHOW_SETTINGS; end else mainstate = SET_INIT; end else begin if (time < 1000) begin lcd_gotoxy(0,1); lcd_putsf("1. Show Current "); end else if (time < 2000) begin lcd_gotoxy(0,1); lcd_putsf("2. Edit Settings"); end else time = 0; end break; case SHOW_SETTINGS: // displays all current customizeable settings TerminateType = 0; if (DoKeypad == 1) RunKeypad(); if (time % 1500 < 5) begin lcd_clear(); sprintf(lcd_buffer,"%i",bpm); // prep line for bpm end else if (time < 1500) begin lcd_gotoxy(0,0); lcd_putsf("Beats per Minute"); // show current beat lcd_gotoxy(0,1); lcd_putsf("BPM = "); lcd_gotoxy(0,6); lcd_puts(lcd_buffer); end else if (time < 3000) begin // grace period on/off lcd_gotoxy(0,0); lcd_putsf("Grace Period is"); lcd_gotoxy(6,1); if (graceOn == 0) lcd_putsf("OFF"); else lcd_putsf("ON"); end else if (time < 4500) begin // show score on/off lcd_gotoxy(0,0); lcd_putsf("Show score is"); lcd_gotoxy(6,1); if (scoreOn == 0) lcd_putsf("OFF"); else lcd_putsf("ON"); sprintf(lcd_buffer,"Tune at %i Hz",tunerfreq); // prep line for tuner end else if (time < 6000) begin // show desired pitch lcd_gotoxy(0,0); lcd_putsf("Tuner Settings"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%i",tunerfreq); lcd_puts(lcd_buffer); end else if (time < 7500) begin // show input play on/off lcd_gotoxy(0,0); lcd_putsf("Play Input is"); lcd_gotoxy(6,1); if (playinputOn == 0) lcd_putsf("OFF"); else lcd_putsf("ON"); end else if (time < 9000) begin // show gender lcd_gotoxy(0,0); lcd_putsf("Input voice is"); lcd_gotoxy(4,1); if (Male == 0) lcd_putsf("FEMALE"); else lcd_putsf("MALE"); end else if (time < 10500) begin // show calcuation mode lcd_gotoxy(0,0); lcd_putsf("Calculation is"); lcd_gotoxy(0,1); if (FFTMode == 0) lcd_putsf("SCHMIDT TRIGGER"); else lcd_putsf("FAST FOURIER"); end else if (time < 12000) begin // show input save on/off lcd_gotoxy(0,0); lcd_putsf("Input Autosave"); lcd_gotoxy(6,1); if (SaveInput == 0) lcd_putsf("OFF"); else lcd_putsf("ON"); end else begin lcd_clear(); mainstate = SIX_INIT; end break; case SET_INIT: // set up settings TerminateType = 0; if (DoKeypad == 1) RunKeypad(); lcd_clear(); //clear the display lcd_gotoxy(0,0); //position to upper left on display lcd_putsf("Setting options"); //string from flash anipos = 0; ValidMenuSelection = 0; AlreadyGotSelection = 0; time = 0; mainstate = SETMENU_WAIT; break; case SETMENU_WAIT: // wait 200 ms before updating scrolling settings menu TerminateType = 2; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); AlreadyGotSelection = 1; menu_selection = atoi(keystr); end if (time >= 200) mainstate = SETMENU_UPDATE; break; case SETMENU_UPDATE: // Menu Update State // count display loop TerminateType = 2; if (DoKeypad == 1) RunKeypad(); if (ValidMenuSelection == 1) begin lcd_clear(); time = 0; if (AlreadyGotSelection == 0) menu_selection = atoi(keystr); if (menu_selection == 1) begin time = 0; mainstate = SETBEAT; end else if (menu_selection == 2) mainstate = SETGRACE; else if (menu_selection == 3) mainstate = SETSCORE; else if (menu_selection == 4) begin ValidMenuSelection = 0; mainstate = SETTUNER; end else if (menu_selection == 5) mainstate = SETINPUT; else if (menu_selection == 6) mainstate = SETGENDER; else if (menu_selection == 7) mainstate = SETLISTENMODE; else if (menu_selection == 8) mainstate = SETSAVE; else if (menu_selection == 9) mainstate = DOCUMENTATION; else // just in case messed up mainstate = SET_INIT; end else begin //scroll the text // set position anipos++; if (anipos < 5) // text not done scrolling across screen begin for (i = 0; i < 16; i++) begin lcd_buffer[i] = SetMenuTitle[i]; end lcd_gotoxy(0,1); lcd_buffer[16] = nullchar[0]; lcd_puts(lcd_buffer); end else if (anipos > SetMenuSize -1) // text is filling up the screen anipos = 0; else begin for (i = 0; i < 16; i++) begin lcd_buffer[i] = SetMenuTitle[i+anipos-5]; end lcd_buffer[16] = nullchar[0]; lcd_gotoxy(0,1); lcd_puts(lcd_buffer); end time = 0; mainstate = SETMENU_WAIT; end break; case SETBEAT: // change bpm settings TerminateType = 4; if (DoKeypad == 1) RunKeypad(); setfrom = SETBEAT; if (time < 500) begin // prompt for bpm lcd_gotoxy(0,0); lcd_putsf("BPM = "); end else begin if (done == 1) begin bpm = atoi(keystr); time = 0; mainstate = CONFIRMATION; end end break; case SETGRACE: // toggle grace period TerminateType = 0; if (DoKeypad == 1) RunKeypad(); setfrom = SETGRACE; if (graceOn == 0) graceOn = 1; else graceOn = 0; time = 0; mainstate = CONFIRMATION; break; case SETSCORE: // toggle show score TerminateType = 0; if (DoKeypad == 1) RunKeypad(); setfrom = SETSCORE; if (scoreOn == 0) scoreOn = 1; else scoreOn = 0; time = 0; mainstate = CONFIRMATION; break; case SETTUNER: // finish settuner... show menu for 1. default or 2. custom // transitions to state... if select default, goto confirmation with tuner freq at 440 // if select default, goto customtuner state... prompt for input.. // then from there, goto confirmation state that displays tuner freq TerminateType = 3; if (DoKeypad == 1) RunKeypad(); setfrom = SETTUNER; if (ValidMenuSelection == 1) begin lcd_clear(); time = 0; if (atoi(keystr) == 1) begin tunerfreq = 440; mainstate = CONFIRMATION; end else mainstate = CUSTOMTUNER; end else begin if (time % 1000 < 5) lcd_clear(); else if (time < 1000) begin lcd_gotoxy(0,1); lcd_putsf("2. Edit Freq"); end else if (time < 2000) begin lcd_gotoxy(0,1); lcd_putsf("1. Use 440 Hz"); end else time = 0; end break; case CUSTOMTUNER: // change tuner frequency TerminateType = 4; if (DoKeypad == 1) RunKeypad(); if (time < 500) begin // prompt for pitch lcd_gotoxy(0,0); lcd_putsf("New Freq = "); end else begin if (done == 1) begin tunerfreq = atoi(keystr); time = 0; mainstate = CONFIRMATION; end end break; case SETINPUT: // toggle play input TerminateType = 0; if (DoKeypad == 1) RunKeypad(); setfrom = SETINPUT; if (playinputOn == 0) playinputOn = 1; else playinputOn = 0; time = 0; mainstate = CONFIRMATION; break; case SETGENDER: // toggle gender settings TerminateType = 0; if (DoKeypad == 1) RunKeypad(); setfrom = SETGENDER; if (Male == 0) Male = 1; else Male = 0; time = 0; mainstate = CONFIRMATION; break; case SETLISTENMODE: // toggle calculation method: FFT or Schmidt Trigger TerminateType = 0; if (DoKeypad == 1) RunKeypad(); setfrom = SETLISTENMODE; if (FFTMode == 0) FFTMode = 1; else FFTMode = 0; time = 0; mainstate = CONFIRMATION; break; case SETSAVE: // toggle save input TerminateType = 0; if (DoKeypad == 1) RunKeypad(); setfrom = SETSAVE; if (SaveInput == 0) SaveInput = 1; else SaveInput = 0; time = 0; mainstate = CONFIRMATION; break; case CONFIRMATION: // display all settings TerminateType = 0; if (DoKeypad == 1) RunKeypad(); if (time < 300) lcd_clear(); else if (time >=300 && time < 1800) begin if (setfrom == SETBEAT) begin lcd_gotoxy(0,0); lcd_putsf("Beats per Minute"); lcd_gotoxy(0,1); sprintf(lcd_buffer,"BPM = %i",bpm); lcd_puts(lcd_buffer); end else if (setfrom == SETGRACE) begin if (graceOn == 0) begin lcd_gotoxy(0,0); lcd_putsf("GRACE PERIOD OFF"); end else begin lcd_gotoxy(0,0); lcd_putsf("GRACE PERIOD ON"); end end else if (setfrom == SETSCORE) begin if (scoreOn == 0) begin lcd_gotoxy(1,0); lcd_putsf("SHOW SCORE OFF"); end else begin lcd_gotoxy(1,0); lcd_putsf("SHOW SCORE ON"); end end else if (setfrom == SETTUNER) begin lcd_gotoxy(0,0); lcd_putsf("Tuner Freq = "); lcd_gotoxy(0,1); sprintf(lcd_buffer,"%i",tunerfreq); lcd_puts(lcd_buffer); end else if (setfrom == SETINPUT) begin if (playinputOn == 0) begin lcd_gotoxy(1,0); lcd_putsf("PLAY INPUT OFF"); end else begin lcd_gotoxy(1,0); lcd_putsf("PLAY INPUT ON"); end end else if (setfrom == SETGENDER) begin if (Male == 0) begin lcd_gotoxy(0,0); lcd_putsf("FOR FEMALE VOICE"); end else begin lcd_gotoxy(1,0); lcd_putsf("FOR MALE VOICE"); end end else if (setfrom == SETLISTENMODE) begin if (FFTMode == 0) begin lcd_gotoxy(1,0); lcd_putsf("SCHMIDT TRIGGER"); end else begin lcd_gotoxy(1,0); lcd_putsf("FAST FOURIER"); end end else if (setfrom == SETSAVE) begin if (SaveInput == 0) begin lcd_gotoxy(1,0); lcd_putsf("SAVE INPUT OFF"); end else begin lcd_gotoxy(1,0); lcd_putsf("SAVE INPUT ON"); end end end else if (time >= 1800 && time < 2100) lcd_clear(); else mainstate = SET_INIT; break; case PLAY_INIT: // set up for PLAY path TerminateType = 0; if (DoKeypad == 1) RunKeypad(); DDRB = 0b10111111; // output to all horizontal lines... and all vertical rows except 3,6,9,#. Read from B.6 PORTB= 0b01110111; // output 0 to all lines on output except for B3 //fast PWM mode, full clock rate, toggle oc0 (pin B3) //16 microsec per PWM cycle implies max freq for 16 samples of // 1/(16e-6*16) = 3900 Hz. //TCCR0 = 0b01101001 ; //turn on timer 0 overflow ISR TIMSK = 0b00000001; // Initialize DDR if (InitP == 1) begin timep = 0; InitP = 0; end TCCR0 = 0b00000001; STOP = 0; bpmfactor = (unsigned int)(7500/bpm); // Set up scrolling notes display // Options 1 & 3 only if (PlayFromFlag != 4) begin numNotes = 0; while (input[numNotes][0] != 0x5b) // find end character begin numNotes++; end smallest_duration = input[0][1]; // let the first entry be the smallest_duration for (x=0;x> 1; toggledot = 1; end else if (smallest_duration == 32) begin sd_shift = 5; modbpm = bpm >> 2; toggledot = 1; end togglecount = 1; // initialize toggle delta_t = bpmfactor*(unsigned int)(smallest_duration); // get the smallest duration in terms of time end if (timep < 1000) {} else begin MainPlayLoopDone = 1; SubPlayLoopDone = 1; timep = 0; x = 0; mainstate = PLAY; end break; case PLAY: // play music TerminateType = 0; if (DoKeypad == 1) RunKeypad(); FromListen = 0; if (input[x][0] != 0x5b && STOP == 0) // go until STOP key hit or end of input begin if (PINB.6 == 0) STOP = 1; if (MainPlayLoopDone == 1) // do this once per note begin if (PlayFromFlag == 1) pitch = ptable[(input[x][0]>>4)+!Male][input[x][0]&0x0f]; else if (PlayFromFlag != 4) pitch = ptable[input[x][0]>>4][input[x][0]&0x0f]; else pitch = tunerfreq; length = input[x][1]; increment = (unsigned long)(floor(pitch/resolution)); TCCR0 = 0b01101001; end if (timep<(bpmfactor*(unsigned int)(length))-30 && SubPlayLoopDone == 1) begin MainPlayLoopDone = 0; // disable get next pitch for now SubPlayLoopPrep = 1; // enable flag for next step if (PlayFromFlag != 4) mainstate = SCROLLING_UPDATE; end else begin if (SubPlayLoopPrep == 1) begin timep = 0; TCCR0 = 0b00000001; SubPlayLoopPrep = 0; // disable own flag, run once end if (timep<30) SubPlayLoopDone = 0; // flag stays disabled else begin SubPlayLoopDone = 1; // re-enable flag timep = 0; //phase lock the sine gen and timer accumulator = 0; TCNT0 = 0; OCR0 = 0; MainPlayLoopDone = 1; // re-enable flag if (PlayFromFlag != 4) x++; end end end else begin increment = 0; // set back to normal DDRB=0xff; TIMSK = 0x02; TCCR0=0b00001011; //prescalar to 64 and turn on clear-on-match OCR0 = 250; //set the compare to 250 time ticks if (PlayFromFlag == 1) mainstate = MENU_INIT; else if (PlayFromFlag == 3) begin InitL = 1; mainstate = LISTEN_INIT; end else if (PlayFromFlag == 4) mainstate = LISTEN_INIT; end break; case DOCUMENTATION: // show documentation TerminateType = 0; // but allows for enter key... if (DoKeypad == 1) RunKeypad(); if (time % 1000 == 0) lcd_clear(); else if (time < 1000) begin lcd_gotoxy(0,0); lcd_putsf("Created by "); lcd_gotoxy(0,1); lcd_putsf("Jerry Chiang"); end else if (time < 2000) begin lcd_gotoxy(0,1); lcd_putsf("and Anderson Lin"); end else if (time > 2000) mainstate = SET_INIT; //(screens of text... move from one to the next by using "enter" key) break; end // end switch end // end while end //end main //********************************************************** void initialize(void) begin lcd_init(LCDwidth); //initialize the display //Init port C to show keyboard result DDRC = 0xff; PORTC = 0xff; DDRB=0xff; DDRD=0xff; //turn on timer 0 COMPARE ISR OCR0 = 250; //set the compare to 250 time ticks TCCR0=0b00001011; //prescalar to 64 and turn on clear-on-match TIMSK = 0x02; //init the sine table for (i=0; i<256; i++) begin sineTable1[i] = 128 + (char)(127.0 * sin(6.283*((float)i)/256.0)) ; end //initialize resolution = 16e6/256/(pow(2,32)); //init key stuff keycount = 0; keyready = 0; done = 1; startover = 1; t = 0; // init settings bpm = 80; graceOn = 1; scoreOn = 1; playinputOn = 0; tunerfreq = 440; STOP = 0; Male = 1; SaveInput = 0; FFTMode = 0; for (i = 0; i < MaxInput; i++) begin inputstore[i][0] = 0; inputstore[i][1] = 0; end // menu initial state mainstate = WELCOME; // debounce initial state nextstate = START; // initialize DoKeypad DoKeypad = 0; #asm sei #endasm end //******************************* void initkey(void) // initialize begin // initialize keycount = 0; keyready = 0; nNumber = 0; nLetter = 0; inRange = 0; for (i = 0; i < KEYSIZE; i++) begin keystr[i] = 0; end end //******************************* void debounce(void) begin switch(nextstate) begin case START: done = 0; donecount = 0; startover = 0; if(butnum != 0 && (mainstate == MENU_WAIT || mainstate == MENU_UPDATE || mainstate >5)) begin maybe = butnum; keypad(); nextstate = DEBOUNCE; end break; case DEBOUNCE: if(butnum == maybe) nextstate = TERMINATOR; else nextstate = START; break; case TERMINATOR: if (butnum == 4 && (TerminateType != 4 || (TerminateType == 4 && keycount == 0))) begin mainstate = MENU_INIT; startover = 1; donecount = DELAYDONE; nextstate = START; end else begin if (!((butnum >= 13 && butnum <= 16) || butnum >= 29)) // runs only for valid butnums begin if (TerminateType == 1) // assuming only 6 menu choices, and 6 song choices for now... begin if (butnum < 11 && (butnum % 4 == 1 || butnum % 4 == 2)) // butnum == 1,2,3,4,5,or 6) begin keystr[keycount] = keypd[butnum-1]; keyready = 1; nextstate = DONE; ValidMenuSelection = 1; end end else if (TerminateType == 2) // assuming only 9 menu choices begin if (butnum < 12 && (butnum % 4 == 1 || butnum % 4 == 2 || butnum % 4 == 3)) // butnum == 1,2,3,4,5,6,7,8,or 9) begin keystr[keycount] = keypd[butnum-1]; keyready = 1; nextstate = DONE; ValidMenuSelection = 1; end end else if (TerminateType == 4) begin if (butnum == enter) begin formatchecker(); if (FormatOK == 1) begin // Format matches. Store and goto DONE keystr[keycount] = 0; keyready = 1; nextstate = DONE; end else begin // Format doesn't match. Erase old entry, wait for new. startover = 1; donecount = DELAYDONE; nextstate = START; lcd_gotoxy(0,1); lcd_putsf(" "); // put in a blank end end else if (butnum == 4) // clear key begin startover = 1; donecount = DELAYDONE; nextstate = START; lcd_gotoxy(0,1); lcd_putsf(" "); // put in a blank end else begin if (butnum >= 24 && (mainstate == THREE_P || mainstate == CUSTOMTUNER)) begin // Entries with Sharp conditions keystr[keycount] = keypd[butnum-1]; keystr[keycount+1] = keypd[32]; lcd_gotoxy(0,1); // position upper left lcd_puts(keystr); // display keystring keycount = keycount+2; // increment keycount end else begin // Normal entries.. numbers or single letters keystr[keycount] = keypd[butnum-1]; // butnum index to string table lcd_gotoxy(0,1); // position upper left lcd_puts(keystr); // display keystring keycount++; // increment keycount end // index into pitch array if (butnum == 17) LetterIndex = 0; else if (butnum == 18) LetterIndex = 2; else if (butnum == 19) LetterIndex = 3; else if (butnum == 20) LetterIndex = 5; else if (butnum == 21) LetterIndex = 7; else if (butnum == 22) LetterIndex = 8; else if (butnum == 23) LetterIndex = 10; else if (butnum == 24) LetterIndex = 1; else if (butnum == 25) LetterIndex = 4; else if (butnum == 26) LetterIndex = 6; else if (butnum == 27) LetterIndex = 9; else if (butnum == 28) LetterIndex = 11; else begin // must be a number nNumber++; oneChar[0] = keypd[butnum-1]; oneChar[1] = nullchar[0]; NumberIndex = (unsigned char)atoi(oneChar) - 1; if (butnum == 1 || butnum == 2 || butnum == 5 || butnum == 6 || butnum == 10 || butnum == 9) inRange = 1; // check number is between else inRange = 0; end if (butnum >= 17 && butnum <= 28) nLetter++; end end else if (TerminateType == 3) begin if (butnum == 1 || butnum == 5) // butnum == 1 or 2) begin keystr[keycount] = keypd[butnum-1]; keyready = 1; nextstate = DONE; ValidMenuSelection = 1; end end else if (TerminateType == 5) // assuming only 10 menu choices begin if (butnum < 12 && (butnum == 8 || butnum % 4 == 1 || butnum % 4 == 2 || butnum % 4 == 3)) // butnum == 0,1,2,3,4,5,6,7,8,or 9) begin keystr[keycount] = keypd[butnum-1]; keyready = 1; nextstate = DONE; ValidMenuSelection = 1; end end nextstate = PRESSED; end end break; case DONE: // use donecount to do something upon first entrance... and then wait awhile before going back to start done = 1; donecount++; startover = 1; if (donecount == DELAYDONE) nextstate = START; break; case PRESSED: if(butnum != maybe) nextstate = RELEASE; break; case RELEASE: if(butnum == maybe) nextstate = PRESSED; else if (keyready == 1) nextstate = DONE; else nextstate = START; end end // need to change to 7 bits... for both of them. DDRB = 0x0f, DDRB = 0xf8? B3/D3 always high? skip over in loop with mod condition //******************************* void keypad(void) begin //get lower nibble DDRB = 0x87; PORTB = 0x78; delay_us(5); keyB = PINB; //get upper nibble DDRB = 0xf0; PORTB = 0x8f; delay_us(5); keyB = keyB | PINB; keyB = keyB | 0x08; //get lower nibble DDRD = 0x87; PORTD = 0x78; delay_us(5); keyD = PIND; //get upper nibble DDRD = 0xf0; PORTD = 0x8f; delay_us(5); keyD = keyD | PIND; keyD = keyD | 0x08; //find matching keycode in keytbl if (keyB != 0xff) begin for (butnumB=0; butnumB= 1 && tmpnum <= 2000) FormatOK = 1; end end else if (mainstate == CUSTOMTUNER) begin tmpnum = atoi(keystr); if (tmpnum >= 27.5 && tmpnum <= 1661.22) FormatOK = 1; end end unsigned char findpitch(float freq) begin unsigned char j, k, n, s, match; float off; off = freq; for (j=0;j<6;j++) begin for (k=0;k<12;k++) begin if (abs((int)(freq - ptable[j][k])) < off) begin s = j; n = k; off = freq - ptable[j][k]; end end end match = (s<<4) | n; return match; end void noterep(unsigned char pitchmatch) begin switch (pitchmatch&0x0f) begin case 0: note[0] = 'A'; note[1] = ''; sharpOn = 0; break; case 1: note[0] = 'A'; note[1] = '#'; sharpOn = 1; break; case 2: note[0] = 'B'; note[1] = ''; sharpOn = 0; break; case 3: note[0] = 'C'; note[1] = ''; sharpOn = 0; break; case 4: note[0] = 'C'; note[1] = '#'; sharpOn = 1; break; case 5: note[0] = 'D'; note[1] = ''; sharpOn = 0; break; case 6: note[0] = 'D'; note[1] = '#'; sharpOn = 1; break; case 7: note[0] = 'E'; note[1] = ''; sharpOn = 0; break; case 8: note[0] = 'F'; note[1] = ''; sharpOn = 0; break; case 9: note[0] = 'F'; note[1] = '#'; sharpOn = 1; break; case 10: note[0] = 'G'; note[1] = ''; sharpOn = 0; break; case 11: note[0] = 'G'; note[1] = '#'; sharpOn = 1; break; default: note[0] = 'X'; note[1] = 'X'; sharpOn = 1; break; end // end switch end /********************************/ void RunKeypad(void) begin DoKeypad = 0; keypad(); debounce(); end