K64F drum machine, with RAW WAV sample playback, and USB midi functionality

Dependencies:   FATFileSystem N5110_mod SDFileSystem USBDevice mbed

Revision:
0:02a88f05d461
--- /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