K64F drum machine, with RAW WAV sample playback, and USB midi functionality
Dependencies: FATFileSystem N5110_mod SDFileSystem USBDevice mbed
Revision 0:02a88f05d461, committed 2016-05-09
- Comitter:
- el14pjgn
- Date:
- Mon May 09 14:20:16 2016 +0000
- Commit message:
- K64F drum machine, with sample playback and USB midi functionality
Changed in this revision
diff -r 000000000000 -r 02a88f05d461 FATFileSystem.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem.lib Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/FATFileSystem/#e960e2b81a3c
diff -r 000000000000 -r 02a88f05d461 N5110_mod.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/N5110_mod.lib Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,1 @@ +https://developer.mbed.org/teams/ELEC2645-201516/code/N5110_mod/#d2140be00144
diff -r 000000000000 -r 02a88f05d461 SDFileSystem.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDFileSystem.lib Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/SDFileSystem/#7b35d1709458
diff -r 000000000000 -r 02a88f05d461 USBDevice.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice.lib Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/USBDevice/#01321bd6ff89
diff -r 000000000000 -r 02a88f05d461 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,706 @@ +# include "main.h" +int main() +{ + init(); /// calls initialisation routine + while(1) { + if(USB_mode == 2) { /// prints display output for speaker mode + lcd.clear(); + lcd.printString("USB audio",0,2); + lcd.printString("to exit,",0,3); + lcd.printString("press back",0,4); + } + while(USB_mode == 2) { ///loop for while within speaker mode + audio->read((uint8_t *)USBaudio_buffer); ///reads audio stream + USBaudio_packet_available = true; ///sets flag to indicate presence of data + if(buttons.back) { ///withdraw from speaker mode + USB_mode = 3; ///set USB mode to none + audio_tkr.detach(); ///detatch audio ticker + LED_scan_tkr.attach(&LED_scan_ISR,0.005); ///re-attach tickers + Sequence_increment_tkr.attach(&Sequence_increment_ISR,30/tempo); + buttons.back = 0; ///re-set back button flag + USB_init(); ///re-initialise USB + lcd.clear(); ///set display to normal + LCD_printMain(); + } + } + check_flags(); ///check for button presses + sleep(); ///return to sleep mode, to save power + } +} + +/* General functions */ + +void init() +{ + LCD_init(); + USB_init(); + Button_init(); + LED_init(); + IO_init(); + tempo_init(); + Sequence_init(); +} + +void IO_init() +{ + for (int i = 0; i < 7; i++) { + trigOut[i]->write(1); /// test trigger outputs + wait(0.1); + trigOut[i]->write(0); + } +} + +int bool_to_int(bool *array, int shifter) +{ + switch(shifter) { + case 0: /// 1:1 mapping + return array[15]+(array[14]*0x2)+(array[13]*0x4)+(array[12]*0x8)+(array[11]*0x10)+(array[10]*0x20)+(array[9]*0x40)+(array[8]*0x80)+(array[7]*0x100)+(array[6]*0x200)+(array[5]*0x400)+(array[4]*0x800)+(array[3]*0x1000)+(array[2]*0x2000)+(array[1]*0x4000)+(array[0]*0x8000); + case 1 : /// even mapping + return (array[7]*0x2)+(array[6]*0x8)+(array[5]*0x20)+(array[4]*0x80)+(array[3]*0x200)+(array[2]*0x800)+(array[1]*0x2000)+(array[0]*0x8000); + case 2: /// odd mapping + return array[7]+(array[6]*0x4)+(array[5]*0x10)+(array[4]*0x40)+(array[3]*0x100)+(array[2]*0x400)+(array[1]*0x1000)+(array[0]*0x4000); + } + return; +} + +void selector(bool *flag, int *variable) +{ + for(int i=0; i<8; i++) { /// scans through input button values + if(buttons.steps[i]) { /// sets value if button is pressed + *variable = i; + buttons.steps[i] = 0; + *flag = 0; + lcd.clear(); ///set display to normal + LCD_printMain(); + } + } +} + +void check_flags() +{ + if(Seq_flag) Sequence_increment(); /// increments sequencer if flag is set + if(decay_flag) { + for(int i = 0; i<8; i++) { /// clears midi messages if decay flag is set + if(USB_mode == 0) midi->write(MIDIMessage::NoteOff(Seq_array[Seq_current_seq].structure.MIDI_note[i])); + } + for(int i = 0; i<7; i++) { /// clears trigger outputs if decay flag is set + trigOut[i]->write(0); + } + decay_tkr.detach(); /// detatches decay ticker + decay_flag = 0; /// clears decay flag + } + if(ButtonFlag_press) { /// run if any input buttons are pressed + /// prints button values over serial if available, for debugging + if(USB_mode == 1) pc->printf("step_0:%d step_1:%d step_2:%d step_3:%d step_4:%d step_5:%d step_6:%d step_7:%d \n",buttons.steps[0],buttons.steps[1],buttons.steps[2],buttons.steps[3],buttons.steps[4],buttons.steps[5],buttons.steps[6],buttons.steps[7]); + if(USB_mode == 1) pc->printf("start:%d skip:%d edit:%d pattern:%d inst:%d save:%d shift:%d load:%d back:%d down:%d enter:%d up:%d \n",buttons.start,buttons.skip,buttons.edit,buttons.pattern,buttons.inst,buttons.save,buttons.shift,buttons.load,buttons.back,buttons.down,buttons.enter,buttons.up); + if(buttons.shift) shift_LED = !shift_LED; /// toggles shift LED + buttons.shift = 0; /// clears shift button flag + //if(buttons.up) buttons.up = 0; /// clears unused button flags + //if(buttons.down) buttons.down = 0; + if(buttons.back) buttons.back = 0; + if(buttons.inst) { + pattern_flag = 0; + inst_flag = !inst_flag; /// toggles instrument selection, and updates LCD & serial + if(inst_flag) { + if(USB_mode == 1) pc->printf("instrument_select\n"); + lcd.printString("select inst.",0,5); + } else { + lcd.clear(); + LCD_printMain(); + } + buttons.inst = 0; + } + if(buttons.pattern) { /// toggles pattern selection, and updates LCD & serial + inst_flag = 0; + pattern_flag = !pattern_flag; + if(pattern_flag) { + if(USB_mode == 1) pc->printf("pattern_select\n"); + lcd.printString("select patrn.",0,5); + } else { + lcd.clear(); + LCD_printMain(); + } + buttons.pattern = 0; + } + if(inst_flag) selector(&inst_flag,&Seq_current_inst); /// selects current instrument if inst_flag is set + else if(pattern_flag) selector(&pattern_flag,&Seq_current_seq); /// selects current pattern if pattern_flag is set + else Sequence_write(); /// otherwise, updates sequence data + LED_write((Seq_array[Seq_current_seq].structure.gate[Seq_current_inst])|(0x8000>>Seq_current_step)); ///writes sequence data & position indicator to matrix + if(buttons.skip) { + if(skip_flag == 0) { + if(USB_mode == 1) pc->printf("skip...\n"); + Seq_prev_inst = Seq_current_inst; /// stores current instrument, in order to return once skip setting is complete + Seq_current_inst = 8; /// sets sequencer channel to skip channel + } else { + if(USB_mode == 1) pc->printf("Done skip\n"); + Seq_current_inst = Seq_prev_inst; /// returns to previous instrument + } + buttons.skip = 0; + skip_flag = !skip_flag; + LED_write((Seq_array[Seq_current_seq].structure.gate[Seq_current_inst])|(0x8000>>Seq_current_step)); ///updates LED output + lcd.clear(); ///re-draws display + LCD_printMain(); + } + if(buttons.start) { ///toggles sequencer ticker + running? Sequence_increment_tkr.detach() : Sequence_increment_tkr.attach(&Sequence_increment_ISR,30/tempo); + lcd.printString(running? "Paused":" ",0,2); + running = !running; + if(running) Sequence_increment_ISR(); + buttons.start = 0; + } + if(buttons.enter) { ///draws main menu + buttons.enter = 0; + mainMenu_draw(); + } + if(buttons.edit) { ///draws edit menu + buttons.edit = 0; + if(skip_flag == 0) editMenu_draw(); + } + if(buttons.load) { ///loads sequence from SD card + load(); + buttons.load = 0; + } + if(buttons.save) { ///saves sequence to SD card + save(); + buttons.save = 0; + } + ButtonFlag_press = 0; ///clears button press flag + } + if(tempo_flag) { ///runs tempo_set routine if button is pressed + tempo_set(); + tempo_flag = 0; + } +} +void null() {} /// no function + +void USB_init() /// initialises USB mode, dependent on USB_mode value +{ + switch (USB_mode) { + case 0: + if(pc!=0) pc->disconnect(); /// disconnects serial, if connected + if(audio!=0) audio->disconnect(); /// disconnects audio, if connected + midi = new USBMIDI; /// connects midi + break; + case 1: + if(midi!=0) midi->disconnect(); /// disconnects midi, if connected + if(audio!=0) audio->disconnect(); /// disconnects audio, if connected + pc = new USBSerial; /// connects serial + break; + case 2: + if(pc!=0) pc->disconnect(); /// disconnects serial, if connected + if(midi!=0) midi->disconnect(); /// disconnects midi, if connected + audio = new USBAudio; /// connects audio + Sequence_increment_tkr.detach(); /// detatches all tickers, to maximise efficiency + LED_scan_tkr.detach(); + audio_tkr.attach_us(audio_tkr_ISR, 1000000.0/(float)48000); + break; + case 3: + if(pc!=0) pc->disconnect(); /// disconnects serial, if connected + if(midi!=0) midi->disconnect(); /// disconnects midi, if connected + break; + } +} +void decay_ISR() /// sets decay flag if called by decay ticker +{ + decay_flag = 1; +} + +/* Button functions */ +void Button_init() /// initialises button matrix +{ + Button_scan_tkr.attach(&Button_scan_ISR,0.02); ///attaches button ticker + ButtonIn_A.fall(&ButtonIn_A_ISR); ///connects button to ISR + ButtonIn_A.mode(PullUp); ///sets pullup + ButtonIn_B.fall(&ButtonIn_B_ISR); + ButtonIn_B.mode(PullUp); + ButtonIn_C.fall(&ButtonIn_C_ISR); + ButtonIn_C.mode(PullUp); + ButtonIn_D.fall(&ButtonIn_D_ISR); + ButtonIn_D.mode(PullUp); + ButtonIn_E.fall(&ButtonIn_E_ISR); + ButtonIn_E.mode(PullUp); +} +void Button_scan_ISR() +{ + if(ButtonFlag_zero == 0) ButtonFlag_cols[ButtonOut_pos] = 0; /// updates repeat flags + ButtonFlag_zero = 0; + ButtonOut_pos++; /// increments button scan position + if(ButtonOut_pos>3) ButtonOut_pos = 0; /// limits scan range + ButtonOut = ButtonOut_val[ButtonOut_pos]; /// writes to output +} +void ButtonIn_A_ISR() +{ + Button_update(0); /// updates button flags +} +void ButtonIn_B_ISR() +{ + Button_update(1); /// updates button flags +} +void ButtonIn_C_ISR() +{ + Button_update(2); /// updates button flags +} +void ButtonIn_D_ISR() +{ + Button_update(3); /// updates button flags +} +void ButtonIn_E_ISR() +{ + Button_update(4); /// updates button flags +} +void Button_update(int row) +{ + ButtonFlag_zero = 1; /// sets button flags + ButtonFlag_press = 1; + if(ButtonFlag_cols[ButtonOut_pos] == 0) { + *(keyMap_point[row][ButtonOut_pos]) = 1; ///sets flag pointed to by position within matrix + ButtonFlag_cols[ButtonOut_pos] = 1; + } +} + +/* LED functions */ +void LED_init() ///initialises LEDs +{ + LED_scan_tkr.attach(&LED_scan_ISR,0.005); ///Setup matrix scan ticker + int LED_test[] { ///output values for test + 0x0,0x180,0x3c0,0x7e0,0xff0,0x1ff8,0x3ffc,0x7ffe,0xFFFF,~0x180,~0x3c0,~0x7e0,~0xff0,~0x1ff8,~0x3ffc,~0x7ffe,0x0 + }; + for(int j=0; j!=17; j++) { ///scan through output test values + LED_write(LED_test[j]); + wait(0.04); + } + shift_LED = 0; /// clear shift LED +} +void LED_scan_ISR() ///Scan ISR +{ + LED_bus = LED_buffer[LED_pos]; ///Sends buffer value to bus output + LED_pos++; ///Increment scan position + if(LED_pos==4) LED_pos = 0; ///limit range +} +void LED_write(uint16_t value) ///Matrix refresh routine +{ + uint8_t LED_scan_row = 0x1; + uint16_t LED_loadBuffer = ((value<<4) | (value>>(16-4))); ///initialise LED_loadBuffer + for(int i=0; i<4; i++) { ///Run loop 4 times + LED_buffer[i] = (~LED_loadBuffer)&0xF0|(LED_scan_row)&0xf; ///write LED_loadBuffer nibble to FSM + LED_scan_row=((LED_scan_row>>1) | (LED_scan_row<<(4-1))); ///Rotate row + LED_loadBuffer=((LED_loadBuffer<<4) | (LED_loadBuffer>>(16-4))); ///rotate LED_loadBuffer + }; +} + +/* LCD functions */ +void LCD_init() /// initialise LCD +{ + lcd.init(); + LCD_set(); + lcd.refresh(); + lcd.printString("Drumulator",1,1); /// display bootscreen + lcd.printString("Ver 2.5PI",25,3); + wait(1); + lcd.clear(); + LCD_printMain(); +} +void LCD_set() +{ + lcd.setPwmFreq(PWM_freq); /// set PWM frequency (new function added to library) + lcd.setBrightness(brightness); /// set brightness (new function added to library) +} +void LCD_printMain() +{ + lcd.drawRect(0,0,84,16,2); /// clear upper section of the screen + char buffer[14]; /// set up buffer for variable printing + int length = sprintf(buffer,"%d bpm",int(tempo)); /// print tempo + if (length <= 14) { + lcd.printString(buffer,0,0); + } + length = sprintf(buffer,"Seq_%d",int(Seq_current_seq)); /// print sequence position + if (length <= 14) { + lcd.printString(buffer,55,0); + } + lcd.printString(inst_names[Seq_current_inst],55,1); /// print current instrument + length = sprintf(buffer,"Stp %d",(Seq_current_step/2)+1); /// print current step + if (length <= 14) { + lcd.printString(buffer,0,1); + } +} + +/* Sequencer functions */ +void Sequence_init() /// initialise sequencer +{ + Sequence_increment_tkr.attach(&Sequence_increment_ISR,30/tempo); +} +void Sequence_increment_ISR() ///Increment sequencer position +{ + Seq_current_step++; + Seq_flag = 1; +} +void Sequence_increment() /// output sequence data +{ + if(Seq_current_step>15) Seq_current_step = 0; + LED_write((Seq_array[Seq_current_seq].structure.gate[Seq_current_inst])|(0x8000>>Seq_current_step)); /// update LED matrix + while((Seq_array[Seq_current_seq].structure.gate[8]&(0x8000>>(Seq_current_step==15? 0 : Seq_current_step+1)) )!=0) { + Seq_current_step++; + if(Seq_current_step>15) Seq_current_step = 0; + } + for(int i = 0; i<8; i++) { + if((Seq_array[Seq_current_seq].structure.gate[i]&(0x8000>>Seq_current_step) )!=0) { + switch (Seq_array[Seq_current_seq].structure.Output_type[i]) { /// update outputs + case 0: /// midi output + if(USB_mode == 0) midi->write(MIDIMessage::NoteOn(int(Seq_array[Seq_current_seq].structure.MIDI_note[i]))); + decay_tkr.attach(&decay_ISR,0.1); + break; + case 1: /// audio output + audio_pos[i] = 0; + audio_flag[i] = 1; + if(USB_mode == 1) pc->printf("audio_flag %d = %d\n",i,audio_flag[i]); + break; + case 2: /// trigger output + trigOut[int(Seq_array[Seq_current_seq].structure.Trig_chan[i])]->write(1); + decay_tkr.attach(&decay_ISR,0.1); + break; + case 3: /// no output + break; + }; + } + } + char buffer[14]; + int length = sprintf(buffer,"Stp %d",(Seq_current_step/2)+1); /// update step position display + if (length <= 14) { + lcd.printString(buffer,0,1); + } + if(tempo_update_flag) { /// set tempo + Sequence_increment_tkr.detach(); ///detach increment ticker + Sequence_increment_tkr.attach(&Sequence_increment_ISR,30/tempo); + tempo_update_flag = 0; + } + Seq_flag = 0; +} +void Sequence_write() /// write button value to gate channel +{ + Seq_array[Seq_current_seq].structure.gate[Seq_current_inst] = Seq_array[Seq_current_seq].structure.gate[Seq_current_inst]^bool_to_int(buttons.steps,(shift_LED+1)); + for(int i = 0; i!=8; i++) { + buttons.steps[i] = 0; + } +} + +/* Tap tempo functions */ +void tempo_init() /// initialise tap tempo +{ + tempo_tapIn.rise(&tempo_ISR); /// attach tap input to ISR + tempo_time.start(); /// start timer +} +void tempo_ISR() //Tap tempo ISR +{ + tempo_flag = 1; +} +void tempo_set() +{ + if(USB_mode == 1) pc->printf("tapped..."); + if(tempo_time.read()>(8*(tempo_timerState+1))) tempo_timerState = 0; + switch(tempo_timerState) { + case 0: ///First press (timer start) + if(tempo_time.read()>DEBOUNCE) { /// to prevent false triggers + if(USB_mode == 1) pc->printf("first tap\n"); + tempo_time.stop(); /// reset and restart timer + tempo_time.reset(); + tempo_time.start(); + tempo_timerState++; /// Increment state variable + if(USB_mode == 1) pc->printf("tap tempo started!\n"); + lcd.printString("Set tempo.",0,5); + } + break; + case 1: ///Second press (count) + if(tempo_time.read()>DEBOUNCE) { /// to prevent false triggers + tempo_timerState++; ///Increment state variable + if(USB_mode == 1) pc->printf("second tap\n"); + tempo_debounce = tempo_time.read(); + lcd.printString(".",59,5); + } + break; + case 2: ///Third press (count) + if((tempo_time.read()-tempo_debounce)>DEBOUNCE) { /// to prevent false triggers + tempo_timerState++; ///Increment state variable + if(USB_mode == 1) pc->printf("third tap\n"); + tempo_debounce = tempo_time.read(); + lcd.printString(".",64,5); + } + break; + case 3: ///Fourth press (Stop timer & set tempo) + if((tempo_time.read()-tempo_debounce)>DEBOUNCE) { /// to prevent false triggers + tempo_time.stop(); ///Stop timer + if(USB_mode == 1) pc->printf("timer stopped..."); + tempo = (180/tempo_time.read()); + tempo_update(); /// update tempo + Seq_current_step = 15; + LCD_printMain(); + if(USB_mode == 1) pc->printf("tempo updated!\n"); + tempo_time.reset(); + tempo_time.start(); ///reset tempo timer + tempo_timerState = 0; ///Reset state variable + tempo_debounce = 0; + lcd.clear(); + LCD_printMain(); + } + } +} +void tempo_update() /// set update flag +{ + if(USB_mode == 1) pc->printf("tempo set..."); + tempo_update_flag = 1; +} + +/* Menu functions */ +void mainMenu_draw() +{ + switch(drawMenu(mainMenu)) { /// draw main menu + case 0: + drawVariable("Tempo",&tempo,5,500,1,&tempo_update); ///set tempo + break; + case 1: + switch(drawMenu(displayMenu)) { /// draw display menu + case 0: + drawVariable("Brightness", &brightness,0.05,1,0,&LCD_set); /// set brightness + break; + case 1: + drawVariable("PWM frequency", &PWM_freq,10,600,0,&LCD_set); /// set PWM frequency + break; + case 2: + inverse=!inverse; /// toggle screen inversion + if(inverse) { + lcd.inverseMode(); /// inverted display + } else { + lcd.normalMode(); /// normal display + } + lcd.clear(); /// reset display + LCD_printMain(); + break; + case 3: + mainMenu_draw(); /// return to main menu when back button is pressed + }; + break; + case 2: + USB_mode = drawMenu(usbMenu); /// select USB mode + USB_init(); + break; + case 3: + break; + } + lcd.clear(); /// reset display + LCD_printMain(); +} +void editMenu_draw() +{ + switch (drawMenu(editMenu)) { /// draw editmenu + case 0: /// set output type + Seq_array[Seq_current_seq].structure.Output_type[Seq_current_inst] = drawMenu(outputMenu); + break; + case 1: /// set note value + drawVariable("Note Value",&Seq_array[Seq_current_seq].structure.MIDI_note[Seq_current_inst],1,127,0,&null); + break; + case 2: /// set trigger channel + drawVariable("trig chan",&Seq_array[Seq_current_seq].structure.Trig_chan[Seq_current_inst],1,6,0,&null); + break; + case 3: + Audio_init(); /// call wave file load procedure + break; + case 4: + break; + } + lcd.clear(); /// reset screen + LCD_printMain(); +} +int drawMenu(const char* array[]) ///draws single level menu on screen +{ + int size=0; + int pos_s=0; + bool data=true; + int menuPos=0; + while(data==true) { /// checks size of array + size++; + if(array[pos_s]==0) { + size--; + data = false; + } + pos_s++; + } + lcd.clear(); /// reset display + LCD_printMain(); + for (int pos=0; pos<size; pos++) { ///loops through menu entries + lcd.printString(array[pos],0,pos+2); ///plots menu entries + } + while(1) { /// scans input flags + if(buttons.up) { + lcd.printString(" ",79,menuPos+2); /// clears menu indicator + (menuPos==0)? menuPos = size-1 : menuPos--; /// increments menu position + if(USB_mode == 1) pc->printf("menuPos %d\n",menuPos); + buttons.up = 0; + } + if(buttons.down) { + lcd.printString(" ",79,menuPos+2); /// clears menu indicator + (menuPos==size-1)? menuPos = 0 : menuPos++; /// decrements menu position + if(USB_mode == 1) pc->printf("menuPos %d\n",menuPos); + buttons.down = 0; + } + lcd.printString("<",79,menuPos+2); /// draw menu indicator + if(buttons.enter|buttons.back) { /// return + buttons.enter = 0; + buttons.back = 0; + lcd.clear(); + LCD_printMain(); + return menuPos; + } + check_flags(); + sleep(); /// sleeps until output + } +} + +void drawVariable(const char* variableName,float *variable, float step_size, float max, float min, void (*function)()) +{ + char numberbuffer[14]; + lcd.clear(); + LCD_printMain(); + while(1) { + lcd.printString(variableName,0,3); /// print variable name + if(buttons.up) { /// increment variable + *variable=*variable+step_size; + buttons.up = 0; + } + if(buttons.down) { /// decrement variable + *variable=*variable-step_size; + buttons.down = 0; + } + if (*variable>max) *variable = max; /// limit range + if (*variable<min) *variable = min; + int length = sprintf(numberbuffer,((*variable<0.1)&&(*variable>-0.1))? ((*variable<0)?"-%d.0%d":"%d.0%d"):((*variable<0)?"-%d.%d":"%d.%d"),abs(int(*variable)),abs(int(*variable*100-(int(*variable)*100)))); + if (length <= 14) { /// print variable + lcd.printString(" ",0,4); + lcd.printString(numberbuffer,2,4); + } + if(buttons.enter|buttons.back) { /// return + buttons.enter = 0; + buttons.back = 0; + lcd.clear(); + LCD_printMain(); + return; + } + (*function)(); + check_flags(); + sleep(); + } +} + +/* File Functions */ + +void save() +{ + lcd.clear(); /// reset display + LCD_printMain(); + lcd.printString("saving...",0,5); + if(USB_mode == 1) pc->printf("saving sequence...\n"); + Seq_array[Seq_current_seq].sequence = new char [sizeof(Seq_array[Seq_current_seq].structure)]; + std::ofstream outfile ("/sd/sequence.mdrum",std::ofstream::binary); /// create file stream + outfile.seekp(0); /// set file position to zero + if(USB_mode == 1) pc->printf("sequence = %d\n",Seq_array[Seq_current_seq].sequence); /// output file data for debugging + outfile.write(Seq_array[Seq_current_seq].sequence,sizeof(Seq_array[Seq_current_seq].sequence)); /// write data to filestream + outfile.close(); /// close file + if(USB_mode == 1) pc->printf("file closed\n"); + lcd.clear(); /// reset display + LCD_printMain(); + buttons.save = 0; +} + +void load() +{ + lcd.clear(); /// reset display + LCD_printMain(); + lcd.printString("loading...",0,5); + if(USB_mode == 1) pc->printf("saving sequence...\n"); + Seq_array[Seq_current_seq].sequence = new char [sizeof(Seq_array[Seq_current_seq].structure)]; + std::ifstream infile ("/sd/sequence.mdrum",std::ifstream::binary); /// setup filestream + infile.seekg(0); /// set to start of file + infile.read(Seq_array[Seq_current_seq].sequence,sizeof(Seq_array[Seq_current_seq].sequence)); /// read file + infile.close(); /// close file + if(USB_mode == 1) pc->printf("file closed\n"); + lcd.clear(); /// reset display + LCD_printMain(); + buttons.load = 0; +} + +/* USB audio functions */ +void audio_tkr_ISR() /// ticker to update USB audio output +{ + float speaker_value; /// speaker value + if (USBaudio_packet_available) { /// checks if packet is available + speaker_value = (float)(USBaudio_buffer[USBaudio_index_buffer]); /// converts value to float + speaker_value = speaker_value + 32768.0; /// ofsets speaker value + speaker_value *= audio->getVolume(); /// adjusts output level + USBaudio_index_buffer++; /// increment buffer position + if (USBaudio_index_buffer == 96/2) { /// checks if buffer is empty + USBaudio_index_buffer = 0; + USBaudio_packet_available = false; + } + } else { + speaker_value = audio_prev_val; /// sets speaker value to previous available value + } + audio_prev_val = speaker_value; + SOUND_OUT_1.write_u16((uint16_t)speaker_value); /// writes speaker value to analog output +} + +/* audio functions */ +void Audio_init() +{ + if(USB_mode == 1) pc->printf("Initialising Audio file\n"); + audio_tkr.detach(); + lcd.clear(); + LCD_printMain(); + lcd.printString("loading WAVs...",0,5); + std::ifstream is (audioFile_map[Seq_current_inst], std::ifstream::binary); + if(is) { /// checks if file is present + if(USB_mode == 1) pc->printf("loading %s\n",audioFile_map[Seq_current_inst]); + is.seekg (0, is.end); + audio_length[Seq_current_inst] = is.tellg(); /// finds length of file + audio_buffer[Seq_current_inst] = new char [(audio_length[Seq_current_inst])]; /// sets buffer size + if(USB_mode == 1) pc->printf("length %d\n",audio_length[Seq_current_inst]); + is.seekg (0, is.beg); + is.read (audio_buffer[Seq_current_inst],audio_length[Seq_current_inst]); /// loads file into buffer + is.close(); /// closes file + } + if(USB_mode == 1) pc->printf("WAVs loaded\n"); + lcd.clear(); + LCD_printMain(); + if(USB_mode == 1){ + for(int i=0;i<audio_length[Seq_current_inst];i++) + { + pc->printf("%d",audio_buffer[Seq_current_inst][i]); + } + } + for(int i=0;i<audio_length[Seq_current_inst];i++) + { + SOUND_OUT_1 = float(int(audio_buffer[Seq_current_inst][i]))/255; + wait(0.00002267573); + } + audio_tkr.attach(Audio_ISR,0.00002267573);/// attaches audio update ticker +} +void Audio_ISR(){ + /*if(audio_pos[Seq_current_inst]<audio_length[Seq_current_inst]){ + SOUND_OUT_1 = float(int(audio_buffer[Seq_current_inst][audio_pos[Seq_current_inst]]))/1800; + audio_pos[Seq_current_inst]++; + }else{ + audio_pos[Seq_current_inst] = 0; + }*/ + float speaker_value; /// speaker value + for(int i = 0; i<8; i++){ + if(audio_flag[i]==1) audio_pos[i]++; + if(audio_pos[i]>=audio_length[i]){ + audio_pos[i] = 0; + audio_flag[i] = 0; + } + } + speaker_value = float(int(audio_buffer[0][audio_pos[0]])+int(audio_buffer[1][audio_pos[1]])+int(audio_buffer[2][audio_pos[2]])+int(audio_buffer[3][audio_pos[3]])+int(audio_buffer[4][audio_pos[4]])+int(audio_buffer[5][audio_pos[5]])+int(audio_buffer[6][audio_pos[6]])+int(audio_buffer[7][audio_pos[7]]))/1800; + if(speaker_value==0){ + speaker_value = audio_prev_val; + }else{ + SOUND_OUT_1 = speaker_value; + audio_prev_val = speaker_value; + } +} \ No newline at end of file
diff -r 000000000000 -r 02a88f05d461 main.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.h Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,604 @@ +/** +@file main.h +@brief Header file containing functions prototypes, defines, and global variables +@brief K64F drum machine project, for ELEC2645 +@author Peter J.G. Nye +@date May 2016 +*/ + +#ifndef MAIN_H +#define MAIN_H + +#define DEBOUNCE 0.2 /*!< Debounce time for tap tempo */ + +#include "mbed.h" +#include "N5110.h" +#include "USBMIDI.h" +#include "USBSerial.h" +#include "USBAudio.h" +#include "SDFileSystem.h" +#include <fstream> + +/** @defgroup group1 Button matrix + * used for the scanning of the UI buttons + * @{ + */ + + +/** # IO & interrupts */ + +/** @brief Output bus used for scanning button matrix */ +BusOut ButtonOut(PTE26,PTE25,PTD3,PTC8); + +/** @brief interrupt pins connected to button matrix outputs */ +InterruptIn ButtonIn_A(PTB11); +InterruptIn ButtonIn_B(PTB3); +InterruptIn ButtonIn_C(PTB2); +InterruptIn ButtonIn_D(PTB10); +InterruptIn ButtonIn_E(PTB20); + +/** @brief ticker to rotate ButtonOut bus */ +Ticker Button_scan_tkr; + +/** # Flags, maps & variables */ + +/** @brief generates boolean flags for the matrix buttons */ +struct buttons_struct { + bool steps[8]; + bool start; + bool skip; + bool edit; + bool pattern; + bool inst; + bool save; + bool shift; + bool load; + bool back; + bool down; + bool enter; + bool up; +}; +buttons_struct buttons; + +/** +@namespace keyMap +@brief indicates name of button pressed +@brief used for serial debugging +*/ +char *keyMap[][4] { + {"Step 7", "Step 6", "Step 5", "Step 4" }, + {"Step 3", "Step 2", "Step 1", "Step 0" }, + {"Load", "save", "Edit", "Instrument"}, + {"Pattern", "Skip", "Shift", "Start", }, + {"Back", "Enter", "Down", "Up" } +}; + +/** @brief used to map the button presses on to the boolean flags */ +bool *keyMap_point[][4] { + {&buttons.steps[7], &buttons.steps[6], &buttons.steps[5], &buttons.steps[4] }, + {&buttons.steps[3], &buttons.steps[2], &buttons.steps[1], &buttons.steps[0] }, + {&buttons.load, &buttons.save, &buttons.edit, &buttons.inst }, + {&buttons.pattern, &buttons.skip, &buttons.shift, &buttons.start }, + {&buttons.back, &buttons.enter, &buttons.down, &buttons.up } +}; + +/** +@namespace ButtonOut_val +@brief Used to set the value of the ButtonOut bus +@brief When scanned through, sets a different value low each cycle +*/ +uint8_t ButtonOut_val[]= { + 0xE, + 0xD, + 0xB, + 0x7 +}; + +/** +@namespace ButtonOut_pos +@brief stores the current position of the ButtonOut scan, in order to select the correct value for the output from the ButtonOut_val array +@brief increments upwards from 0 to 3, and then resets +*/ +uint8_t ButtonOut_pos = 0; + +/** @brief used to indicate if any button has been pressed */ +bool ButtonFlag_zero = 0; + +/** @brief used to indicate if the input on the column during the last scan was equal to zero */ +bool ButtonFlag_cols[4] = {0,0,0,0}; +bool ButtonFlag_press = 0; + +/** # Functions */ + +/** +@namespace Button_init +@brief function used to initialise the button matrix +*/ +void Button_init(); + +/** +@namespace Button_scan_ISR +@brief function called by Button_scan_tkr +@brief rotates the output of ButtonOut +*/ +void Button_scan_ISR(); + +/** +@namespace Button_update +@brief Used to update the boolean flags within Buttons_struct +@brief called by ButtonIn_*_ISR +@param row - Used to indicate which ButtonIn interrupt it was called by +*/ +void Button_update(int row); + +/** @brief interrupt subroutines for ButtonIn_* input */ +void ButtonIn_A_ISR(); +void ButtonIn_B_ISR(); +void ButtonIn_C_ISR(); +void ButtonIn_D_ISR(); +void ButtonIn_E_ISR(); + +/** @} */ // end of group1 + +/** @defgroup group2 LED matrix + * Variables and functions used to adress the step LEDs + * @{ + */ + +/** # IO & interrupts */ + +/** @brief bus used to adress the LED matrix */ +BusOut LED_bus(PTC0,PTC9,PTC5,PTC7 , PTC2,PTA2,PTB23,PTA1); + +/** @brief ticker used to scan through the LED matrix values */ +Ticker LED_scan_tkr; + +/** +@namespace shift_LED +@brief LED above the shift button on the PCB +@brief not connected to the LED matrix +*/ +DigitalOut shift_LED(PTE24); + +/** # Flags, maps & variables */ + +/** +@namespace LED_buffer +@brief array used to store the requred ouput values for LED_bus +@brief used to reduce the amount of functions to be run within the LED_scan_ISR +*/ +int LED_buffer[4] = {0x7,0xB,0xD,0xE}; + +//uint16_t LED_loadBuffer = 0; + +/** +@namespace LED_pos +@brief stores the current position of the LED_bus scan, in order to select the correct value for the output from the LED_buffer array +@brief increments upwards from 0 to 3, and then resets +*/ +int LED_pos = 0; + +/** @brief */ +//uint8_t LED_scan_row = 0x1; + +/** # LED functions */ + +/** @brief ISR for LED matrix scan */ +void LED_scan_ISR(); + +/** +@namespace LED_write +@brief updates the values within LED_buffer to those required for the current output +@param value - data to be written to the LED matrix +*/ +void LED_write(uint16_t value); //Loads buffer +void LED_init(); + +/** @} */ // end of group2 + +/** @defgroup group3 LCD + * Variables and functions used to adress the nokia 5110 LCD + * @{ + */ + +/** # IO */ + +/** @brief defines LCD pins */ +N5110 lcd (PTE26 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3); + +/** # LCD variables */ + +/** @brief current backlight brightness */ +float brightness = 0.32; + +/** @brief current backlight PWM frequency */ +float PWM_freq = 50; + +/** @brief boolean to keep track of screen colour inversion */ +bool inverse = false; + +/** # LCD functions */ + +/** @brief function to initialise screen */ +void LCD_init(); + +/** @brief function to set brightness and PWM frequency values */ +void LCD_set(); +/** @brief function to print main screen to display */ +void LCD_printMain(); + +/** @} */ // end of group3 + +/** @defgroup group4 Sequencing + * Variables and functions used to adress the step sequencer + * @{ + */ + +/** # Sequence data */ + +/** @brief channel names (to be displayed to LCD) */ +char *inst_names[]= { + "Kick", + "Snare", + "Crash", + "HH Cl", + "HH Op", + "Clap", + "Ride", + "Cowbl", + "Skip" +}; + +/** +@namespace Sequence_struct +@brief generates the variables requred for a sequence +@brief Output_type indicates the type of output set for each channel +@brief Midi_note indicates the note ouput via midi for each channel +@brief Trig_chan indicates the hardware trigger output for each channel +@brief gate contains the sequence data for each channel +@brief final channel of the gate array contains step skip points +@brief the data for each part of these arrays had to be set individually, due to limitations of the MBed IDE +*/ +union Sequence_struct { + struct structure { + char Output_type[8]; + float MIDI_note[8]; + float Trig_chan[8]; + uint16_t gate[9]; + } structure; + char* sequence; + Sequence_struct::Sequence_struct() { + structure.Output_type[0] = 0; + structure.Output_type[1] = 0; + structure.Output_type[2] = 0; + structure.Output_type[3] = 0; + structure.Output_type[4] = 0; + structure.Output_type[5] = 0; + structure.Output_type[6] = 0; + structure.Output_type[7] = 0; + structure.MIDI_note[0] = 36; + structure.MIDI_note[1] = 38; + structure.MIDI_note[2] = 49; + structure.MIDI_note[3] = 42; + structure.MIDI_note[4] = 56; + structure.MIDI_note[5] = 39; + structure.MIDI_note[6] = 37; + structure.MIDI_note[7] = 51; + structure.Trig_chan[0] = 0; + structure.Trig_chan[1] = 1; + structure.Trig_chan[2] = 2; + structure.Trig_chan[3] = 3; + structure.Trig_chan[4] = 3; + structure.Trig_chan[5] = 4; + structure.Trig_chan[6] = 5; + structure.Trig_chan[7] = 6; + }; +} Seq_array[8]; + +/** # Sequence IO & interrupts */ + +/** @brief ticker to increment sequence position */ +Ticker Sequence_increment_tkr; + +/** # flags & variables */ + +/** @brief currently selected instrument channel */ +int Seq_current_inst = 0; +/** @brief currently selected sequence */ +int Seq_current_seq = 0; +/** @brief previously selected instrument channel (used when skip button is pressed) */ +int Seq_prev_inst = 0; +/** @brief current sequence step */ +int Seq_current_step = 15; +/** @brief flag to indicate step increment */ +volatile int Seq_flag = 0; +/** @brief current tempo value */ +float tempo = 60; +/** @brief flag to indicate if the sequence is currently running */ +bool running = 1; +/** @brief indicates whether system is waiting for instrument channel selection */ +bool inst_flag = 0; +/** @brief indicates whether system is waiting for sequence pattern selection */ +bool pattern_flag = 0; +/** @brief indicates whether in skip setup mode */ +bool skip_flag = 0; + +/** # Sequence functions */ + +/** @brief initialises sequencer */ +void Sequence_init(); + +/** @brief writes input data to sequence gate */ +void Sequence_write(); +/** @brief interrupt call for sequence increment */ +void Sequence_increment_ISR(); +/** @brief increments sequencer */ +void Sequence_increment(); + +/** @} */ // end of group4 + +/** @defgroup group5 Tap tempo + * Variables and functions used to control the tap-tempo + * @{ + */ + +/** # IO & interrupts */ + +/** @brief timer to keep track of time between button presses */ +Timer tempo_time; +/** @brief interrupt for tap tempo button */ +InterruptIn tempo_tapIn(PTB9); + +/** # flags & variables */ + +/** @brief set when tap tempo button is pressed */ +volatile int tempo_flag; +/** @brief indicates how many times tap tempo button has been pressed */ +int tempo_timerState; +/** @brief keeps track of button bounces */ +float tempo_debounce = 0; +/** @brief indicates that tempo has been changed */ +volatile int tempo_update_flag = 0; + +/** # functions */ + +/** @brief initialises tap tempo */ +void tempo_init(); +/** @brief interrupt called by tap tempo input */ +void tempo_ISR(); +/** @brief tempo set routine */ +void tempo_set(); +/** @brief Indicates tempo to be updated (used when tempo is set via menu) */ +void tempo_update(); + +/** @} */ // end of group5 + +/** @defgroup group6 Audio output + * Variables and functions used in the playback of wav files (not currently functional) + * @{ + */ + +/** # flags & variables */ + +/** @brief buffer to contain audio file data */ +char * audio_buffer[8]; + +/** @brief map to indicate positions of RAW WAV files on SD card */ +char* audioFile_map[8] { + "/sd/samples/808/Bass.wav", + "/sd/samples/808/Snare.wav", + "/sd/samples/808/Crash.wav", + "/sd/samples/808/HiHat_closed.wav", + "/sd/samples/808/HiHat_open.wav", + "/sd/samples/808/Clap.wav", + "/sd/samples/808/Ride.wav", + "/sd/samples/808/CowBell.wav" +}; +/** @brief indicates whether file is currently playing */ +bool audio_flag[8] {0,0,0,0,0,0,0,0}; +/** @brief indicates file length */ +int audio_length[8] {0,0,0,0,0,0,0,0}; +/** @brief indicates current position within file */ +int audio_pos[8] {0,0,0,0,0,0,0,0}; + +/** # functions */ +/** @brief loads file data into buffers */ +void Audio_init(); +/** @brief updates speaker output */ +void Audio_ISR(); + +/** @} */ // end of group6 + +/** @defgroup group7 USB + * Variables and functions used to control the USB interfacing + * @{ + */ + +/** # flags & variables */ +/** @brief keeps track of current USB mode (0 = midi, 1 = serial, 2 = speaker, 3 = none) */ +int USB_mode = 3; +/** @brief indicates whether the output needs clearing */ +volatile int decay_flag = 0; + +/** # IO & interrupts */ + +/** @brief pointer to midi function */ +USBMIDI *midi = 0; +/** @brief pointer to serial function */ +USBSerial *pc = 0; +/** @brief pointer to audio function */ +USBAudio *audio = 0; +/** @brief ticker for note decay */ +Ticker decay_tkr; + + +/** # functions */ + +/** @brief initialises USB */ +void USB_init(); +/** @brief called by decay ticker */ +void decay_ISR(); + +/** @} */ // end of group7 + +/** @defgroup group8 USB audio + * Variables and functions used to act as a USB soundcard + * (heavily based on existing code, [see here](https://developer.mbed.org/users/samux/code/USBAUDIO_speaker/)) + * @{ + */ + +/** # flags & variables */ + +/** @brief ticker for audio output */ +Ticker audio_tkr; +/** @brief buffer to contain incoming audio packets */ +int16_t USBaudio_buffer[96/2]; +/** @brief indicates availability of audio data */ +volatile bool USBaudio_packet_available = false; +/** @brief indicates current position within USBaudio_buffer */ +int USBaudio_index_buffer = 0; +/** @brief maintains previous speaker value until more data is available */ +uint16_t audio_prev_val = 0; + +/** # USB audio functions */ +/** @brief ISR for audio ouput */ +void audio_tkr_ISR(); + +/** @} */ // end of group8 + +/** @defgroup group9 Menu system + * Variables and functions used to control menus + * based on menu system from first-year project + * @{ + */ + +/** # Menu maps */ + +/** @brief map for Main menu */ +const char *mainMenu[]= { + "Tempo", + "Display", + "USB mode",0 +}; +/** @brief map for display submenu */ +const char *displayMenu[]= { + "Brightness", + "PWM frequency", + "Invert",0 +}; +/** @brief map for USB mode submenu */ +const char *usbMenu[]= { + "MIDI", + "Serial", + "Speaker", + "None",0 +}; +/** @brief map for output mode submenu */ +const char *outputMenu[] = { + "Midi", + "WAV", + "Trigger", + "Muted",0 +}; +/** @brief map for edit menu */ +const char *editMenu[] = { + "Output type", + "Midi Note", + "Trig chan", + "WAV load",0 +}; + +/** # functions */ + +/** @brief function to control main menu navigation */ +void mainMenu_draw(); +/** @brief function to control edit menu navigation */ +void editMenu_draw(); +/** +@namespace drawMenu +@brief draws menu to LCD, and returns selected value +@param array[] - menu map to be drawn + */ +int drawMenu(const char* array[]); +/** +@namespace drawVariable +@brief draws variable to LCD +@param variableName - name of variable being edited +@param variable - pointer to variable being edited +@param step_size - amount to increment variable per buttonpress +@param max - maximum value of variable +@param min - minimum value of variable +@param function - function to be called to update variables + */ +void drawVariable(const char* variableName,float *variable, float step_size, float max, float min, void (*function)()); + +/** @} */ // end of group9 + +/** @defgroup group10 Filesystem + * Variables and functions used to control file management + * (only partially functional) + * @{ + */ +/** # IO */ + +/** @brief IO for SD card interfacting */ +SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd"); //SD card + +/** # Functions */ + +/** @brief loads current sequence data from SD card */ +void load(); +/** @brief saves current sequence data to SD card */ +void save(); + +/** @} */ // end of group10 + +/** @defgroup group11 general + * General purpose variables and functions + * @{ + */ + +/** # IO */ + +/** @brief speaker output */ +AnalogOut SOUND_OUT_1(DAC0_OUT); +/** @brief trigger channels */ +DigitalOut *trigOut[7] { + new DigitalOut(PTC10), + new DigitalOut(PTC11), + new DigitalOut(PTC1), + new DigitalOut(PTC19), + new DigitalOut(PTC18), + new DigitalOut(PTC17), + new DigitalOut(PTC16) +}; + +/** # functions */ + +/** @brief initialises system */ +void init(); +/** @brief initialises general IO*/ +void IO_init(); +/** +@namespace bool_to_int +@brief converts boolean array to integer +@param array - boolean input +@param shifter - integer output + */ +int bool_to_int(bool *array, int shifter); +/** +@namespace selector +@brief selects value between 0 and 7 based on step button press +@param flag - flag to reset after selection is made +@param shifter - variable to be changed + */ +void selector(bool *flag, int *variable); + +/** @brief checks interrupt flags */ +void check_flags(); +/** @brief null function; used to pad drawVariable when no repeated function call is needed +void null(); + +/** @} */ // end of group11 + +#endif
diff -r 000000000000 -r 02a88f05d461 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon May 09 14:20:16 2016 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/aae6fcc7d9bb \ No newline at end of file