For MAX32630FTHR microcontroller: Plays piano notes in Nokia composer format text file placed in the SD card

Dependencies:   USBMSD_BD SDFileSystem max32630fthr USBDevice

main.cpp

Committer:
Lugs
Date:
2019-07-19
Revision:
4:24086b80928e
Parent:
3:fcf745cd4f6d
Child:
5:8ba2f1e291b9

File content as of revision 4:24086b80928e:

#include "mbed.h"
#include "max32630fthr.h"
#include "USBSerial.h"
#include "stdio.h"
#include "SDFileSystem.h"
#include "ctype.h"

#define BUFFER_SIZE 128
#define HALF_BUFFER 64

DigitalOut rLED(LED1);
DigitalOut gLED(LED2);
DigitalOut bLED(LED3);

DigitalIn Button(P2_3);

MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);
PwmOut PWM(P5_6);
AnalogIn POT(AIN_0);
volatile int bufferPOS = 0;

Serial daplink(P2_1,P2_0);
USBSerial microUSB;

float audioDataBuffer[BUFFER_SIZE];

struct WavFile {
    long int size;
    int channels;
    int sampleRate;
    int bitsPerSample;
};

float potval,reading;

void placeNewSample(void)
{
    PWM.write(audioDataBuffer[bufferPOS++]); //multiply by POT value for volume.
    bufferPOS = (bufferPOS+1) & 0x07F;
}

int main()
{
    WavFile Track;
    Ticker SampleTime;

    daplink.printf("\f---DAPLINK SERIAL PORT---\r\n\r\nMINI PIANO PLAYER ver 1 \r\n\r\n\r\n");
    microUSB.printf("micro USB serial port\r\n");
    rLED = LED_ON;
    wait_ms(500);
    rLED = LED_OFF;
    gLED = LED_ON;
    wait_ms(500);
    bLED = LED_ON;
    gLED = LED_OFF;

    typedef enum {
        C2,Cs2,D2,Ds2,E2,F2,Fs2,G2,Gs2,A2,As2,B2,   //C2:0
        C3,Cs3,D3,Ds3,E3,F3,Fs3,G3,Gs3,A3,As3,B3,   //C3:12
        C4,Cs4,D4,Ds4,E4,F4,Fs4,G4,Gs4,A4,As4,B4,   //C4:24
        C5,Cs5,D5,Ds5,E5,F5,Fs5,G5,Gs5,A5,As5,B5,   //C5:36
        C6,Cs6,D6,Ds6,E6,F6,Fs6,G6,Gs6,             //C6:48
        rest,
        END
    } pitchname;

    typedef struct {
        int length;
        pitchname pitch;
    } note;

    //input iterator vars
    int i;
    char c;
    
    //input holder arrays
    char buffer[256];
    char inputtype[30];
    typedef enum {
        nokiacomposer,
        normal
    } inputtypes;
    inputtypes current_inputtype;

    //parse input type
    int g;
    tryagain:
    printf("Please specify the input type.\r\nAvailable types:\r\nnokiacomposer\r\nnormal\r\n\r\n>");
    for(i=0; i<30; i++) {
        c=daplink.getc();
        if(c == '\r')
        {
            g=i+2;
            inputtype[i] = '\0';
            break;
        }
        daplink.putc(c);
        inputtype[i]=c;
    }
    printf("\r\n");
    for(i=0;i<=g;i++)
    {
        printf("%i\t",inputtype[i]);
    }
    printf("\r\nSTRING LITERAL:\r\n");
    char test[] = "nokiacomposer";
    for(i=0;i<=g;i++)
    {
        printf("%i\t",test[i]);
    }

    if(!strcmp(inputtype,"nokiacomposer")) {
        printf("\r\nType chosen: nokiacomposer\r\n");
        current_inputtype = nokiacomposer;
    } else if(!strcmp(inputtype,"normal")) {
        printf("\r\nType chosen: normal\r\n");
        current_inputtype = normal;
    } else {
        c=NULL;
        printf("Invalid input type. Try again.");
        goto tryagain; //prints message below input area
    }

    //open file
toolarge:
    printf("Please copy the text file of the song then right click on the PuTTY screen.\r\n");
    for(i=0; 1; i++) { //do the loop at least once
        c=daplink.getc();
        daplink.putc(c);
        buffer[i]=c;
        if(i>=256) {
            printf("Note data is greater than 2kB! Impossible. Try again.\033[A");
            goto toolarge;
        }
        wait_ms(1); //wait for the next byte to arrive. every byte takes 1.04ms at 9600 baud.
        if(!daplink.readable())
        {
            printf("That'sallll\r\n");
            break;   
        }
    }
    buffer[i] = ' '; //set EOF marker.
    buffer[i++] = '\0';

    //kunwari open na yung file
    printf("\r\nFile recieved. Parsing...\r\n");
    //parse file into song in heap(remove sharps, put into enums.)
    
    //useful benchmarks to find which characters are valid.
    int candidate_number;
    char *songpos=buffer+1; //song position
    note song[256];
    int pn_det;

    if(current_inputtype == nokiacomposer) {
        //for every [note] slot in [song]
        for(i=0; i<2048; i++) {
            //take the first base 10 integer you see.
            //this initializes songpos ALWAYS. lol.
            candidate_number = strtol(songpos-1,&songpos,10);
            printf("Character:%i\r\n",candidate_number);
            //if it's 1,2,4,8,16 or 32, VALID.
            if((candidate_number==1)||candidate_number==2||candidate_number==4||candidate_number==8||candidate_number==16||candidate_number==32) {
                song[i].length = candidate_number;
                printf("Entered candidate [%i] into song[%i].length\r\n",candidate_number,i);
            } else {
                printf("Found invalid NOTE LENGTH value [%c] at position %li. Skipping until next whitespace...\r\n",*songpos,songpos-buffer);
                //skip to after next whitespace
                do {
                    songpos++;
                } while(!isspace(*(songpos)));
                do {
                    songpos++;
                } while(!isalnum(*(songpos)));
                //then do next iteration of LENGTH,PITCH,OCTAVE parse
                i--;
                continue;
            }
            //parse next character
            printf("Character:%c\r\n",*songpos);
            pn_det=0;
            //watch out for sharps
            if(!(*songpos=='#'||'a'<=*songpos<='z')) {
                printf("Found invalid PITCH NAME value [%c] at position %li. Skipping word...\r\n",*songpos,songpos-buffer);
                //skip to after next whitespace
                do {
                    songpos++;
                } while(!isspace(*(songpos)));
                do {
                    songpos++;
                } while(!isalnum(*(songpos)));
                //then do next iteration of LENGTH,PITCH,OCTAVE parse
                i--;
                continue;
            }
            if(*songpos=='#') {
                pn_det+=1;
                songpos++;
                printf("Sharp found.\r\n");
            }
            switch(*songpos) {
                case 'c':
                    pn_det+=0;
                    break;
                case 'd':
                    pn_det+=2;
                    break;
                case 'e':
                    pn_det+=4;
                    break;
                case 'f':
                    pn_det+=5;
                    break;
                case 'g':
                    pn_det+=7;
                    break;
                case 'a':
                    pn_det+=9;
                    break;
                case 'b':
                    pn_det+=11;
                    break;
            }
            //check for invalid character at current position
            //means that character was valid. move on.
            songpos++;
            printf("%c\n",*songpos);
            if('0'<=*songpos<='9') {
                int num = strtol(songpos,&songpos,10);
                num+=3;//shift up thrice. board can't handle things this low.
                printf("octave parsed: %i \t-> adder:%i\r\n",num,((num-2)*12));
                pn_det=pn_det+((num-2)*12);
            } else {
                printf("Found invalid OCTAVE value [%c] at position %li. Skipping word...\r\n",*songpos,songpos-buffer);
                //skip to after next whitespace
                while(!isspace(*(songpos))) {
                    songpos++;
                }
                while(!isalnum(*(songpos))) {
                    songpos++;
                }
                //then do next iteration of LENGTH,PITCH,OCTAVE parse
                i--;
                continue;
            }
            song[i].pitch = (pitchname)pn_det;
            printf("Entered pitch [%i] into song[%i].pitch\r\n",song[i].pitch,i);
            printf("Final: {%i,%i} at i=%i\r\n",song[i].length,song[i].pitch,i);
            printf("-------------------\r\n");
            //make sure you're after the next whitespace
            while(!isspace(*(songpos))) {
                songpos++;
            }
            while(isspace(*(songpos))) {
                songpos++;
            };
            if(*songpos == '\0'){
                printf("NULL found. Escaping...\r\n");
                goto esc;
            }
        }
    } else if(current_inputtype == normal) {
        //add normaltype parser here.
    }

    //add end term

    //parsing is done
esc:

    printf("at end: i = %i\r\n",i);
    i++;
    song[i].length = 1;
    song[i].pitch = END;
    
    printf("Parsing done.\r\n");
    
    //ENDOFPARSER
    
    int k;
    for(k=0; song[k].pitch != END; k++) {
        printf("{%i,%i},",song[k].length,song[k].pitch);
    }
    printf("{%i,%i},",song[k].length,song[k].pitch);
    printf("\r\nPrinting done.\r\n");

    printf("Generating sine...\r\n");
    for(i=0; i<128; i++) {
        audioDataBuffer[i] =((1.0 + sin((double(i)/16.0*6.28318530717959)))/2.0); //formula copied from mbed example
    }

    /* HERE'S WHERE THE MUSIC STARTS */
    /* LITERALLY. */

    //FORMAT:  { NUM_BEATS, PITCH }
    char bpminp[5];
    int PlayingFreq;


    printf("Please enter desired BPM:\r\n");

    for(i=0; i<5 && c!='\r'; i++) {
        c = daplink.getc();
        daplink.putc(c);
        bpminp[i] = c;
    }
    
    printf("BPM Recieved.\r\n");
    
    c=NULL; //reset C for next time.
    int BPM = strtol(bpminp,NULL,0);
    float SPB = 60*4/(float)BPM;
    
    printf("BPM: %i",BPM);
    
restart:

    /*
    HARVEST MOON SONG IN NORMAL FORMAT

    note song[] = {
        //batch 1
        {4,(pitchname)38},{4,E5},{4,Fs5},{4,D5},{4,Fs5},{4,G5},{4,A5},{4,Fs5},{4,D5},
        {4,A5},{4,B5},{4,A5},{4,G5},{4,B5},{4,A5},
        {4,rest},
        //batch 2
        {4,D5},{4,E5},{4,Fs5},{4,D5},{4,Fs5},{4,G5},{4,A5},{4,Fs5},{4,D5},
        {4,A5},{4,G5},{4,D5},{4,Fs5},{4,D5},{4,E5},
        {4,rest},
        //batch 1
        {4,D5},{4,E5},{4,Fs5},{4,D5},{4,Fs5},{4,G5},{4,A5},{4,Fs5},{4,D5},
        {4,A5},{4,B5},{4,A5},{4,G5},{4,B5},{4,A5},
        {4,rest},
        //batch 3
        {2,G5},{4,G5},{4,E5},{4,Fs5},{4,E5},{4,D5},{4,Cs5},{4,D5},
        //END
        {1,END}
    };
    */

    for(i = 0; 1; i++) {
        switch(song[i].pitch) {
            case rest:
                wait(song[i].length*SPB);
                continue;
            case D2:
                PlayingFreq = 73;
                break;
            case Ds2:
                PlayingFreq = 78;
                break;
            case E2:
                PlayingFreq = 82;
                break;
            case F2:
                PlayingFreq = 87;
                break;
            case Fs2:
                PlayingFreq = 92;
                break;
            case G2:
                PlayingFreq = 98;
                break;
            case Gs2:
                PlayingFreq = 104;
                break;
            case A2:
                PlayingFreq = 110;
                break;
            case As2:
                PlayingFreq = 117;
                break;
            case B2:
                PlayingFreq = 123;
                break;
            case C3:
                PlayingFreq = 131;
                break;
            case Cs3:
                PlayingFreq = 139;
                break;
            case D3:
                PlayingFreq = 147;
                break;
            case Ds3:
                PlayingFreq = 156;
                break;
            case E3:
                PlayingFreq = 165;
                break;
            case F3:
                PlayingFreq = 175;
                break;
            case Fs3:
                PlayingFreq = 185;
                break;
            case G3:
                PlayingFreq = 196;
                break;
            case Gs3:
                PlayingFreq = 208;
                break;
            case A3:
                PlayingFreq = 220;
                break;
            case As3:
                PlayingFreq = 233;
                break;
            case B3:
                PlayingFreq = 247;
                break;
            case C4:
                PlayingFreq = 262;
                break;
            case Cs4:
                PlayingFreq = 277;
                break;
            case D4:
                PlayingFreq = 294;
                break;
            case Ds4:
                PlayingFreq = 311;
                break;
            case E4:
                PlayingFreq = 330;
                break;
            case F4:
                PlayingFreq = 349;
                break;
            case Fs4:
                PlayingFreq = 370;
                break;
            case G4:
                PlayingFreq = 392;
                break;
            case Gs4:
                PlayingFreq = 415;
                break;
            case A4:
                PlayingFreq = 440;
                break;
            case As4:
                PlayingFreq = 466;
                break;
            case B4:
                PlayingFreq = 494;
                break;
            case C5:
                PlayingFreq = 523;
                break;
            case Cs5:
                PlayingFreq = 554;
                break;
            case D5:
                PlayingFreq = 587;
                break;
            case Ds5:
                PlayingFreq = 622;
                break;
            case E5:
                PlayingFreq = 659;
                break;
            case F5:
                PlayingFreq = 698;
                break;
            case Fs5:
                PlayingFreq = 740;
                break;
            case G5:
                PlayingFreq = 784;
                break;
            case Gs5:
                PlayingFreq = 831;
                break;
            case A5:
                PlayingFreq = 880;
                break;
            case As5:
                PlayingFreq = 932;
                break;
            case B5:
                PlayingFreq = 988;
                break;
            case C6:
                PlayingFreq = 1047;
                break;
            case Cs6:
                PlayingFreq = 1109;
                break;
            case D6:
                PlayingFreq = 1175;
                break;
            case Ds6:
                PlayingFreq = 1245;
                break;
            case E6:
                PlayingFreq = 1319;
                break;
            case F6:
                PlayingFreq = 1397;
                break;
            case Fs6:
                PlayingFreq = 1480;
                break;
            case G6:
                PlayingFreq = 1568;
                break;
            case Gs6:
                PlayingFreq = 1661;
                break;
            case END:
                i = 0;
                printf("SONG END. PRESS BUTTON TO RESTART.");
                while(Button)
                {
                    printf("\033[A\033[A\033[A\033[A");
                }
                goto restart;
        }

        Track.sampleRate = PlayingFreq * 16; //TONE FREQ = SAMPLE RATE / SAMPLES PER CYCLE
        PWM.period_us(1); //1MHz
        float ticker_period = (float) 1/(Track.sampleRate);
        printf("\r\nTicker Period: %f\tTicker Freq: %f\r\nTarget Freq: %i \r\n\r\n",ticker_period, 1/ticker_period, PlayingFreq);

        SampleTime.attach(&placeNewSample,ticker_period);
        wait( (1/(float)song[i].length) *SPB);
        SampleTime.detach();
        
        

        printf("\033[A\033[A\033[A\033[A");
    }
}