Lawrence Quizon / Mbed OS mini_piano_player

Dependencies:   USBMSD_BD SDFileSystem max32630fthr USBDevice

main.cpp

Committer:
Lugs
Date:
2019-07-23
Revision:
8:ce16aa4cdb6a
Parent:
7:412761dab446
Child:
9:17de551d2208

File content as of revision 8:ce16aa4cdb6a:

#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
#define OS_MAINSTKSIZE 1024


bool debugState = 1;

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;
int i;

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;
}

void presence()
{
    rLED = LED_ON;
    wait_ms(500);
    rLED = LED_OFF;
    gLED = LED_ON;
    wait_ms(500);
    bLED = LED_ON;
    gLED = LED_OFF;
}

typedef enum : unsigned char {
    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 {
    unsigned char length;
    pitchname pitch;
} note;

char *skipToNextEntry(char *songpos, bool skiptype)
{
    if(skiptype = 0) {
        do {
            songpos++;
        } while(!isspace(*(songpos)));
        do {
            songpos++;
        } while(!isalnum(*(songpos)));
        return songpos;
    } else if(skiptype = 1) {
        while(!isspace(*(songpos))) {
            songpos++;
        }
        while(isspace(*(songpos))) {
            songpos++;
        };
    }
}

bool songParse(note *song,char *buffer)
{
    int candidate;
    char *songpos=buffer+1; //song position
    int pn_det;
    int i;
    for(i=0; i<256; i++) {
        //take the first base 10 integer you see.
        //this initializes songpos ALWAYS. lol.
        candidate = strtol(songpos-1,&songpos,10);

        if(debugState) {
            printf("Character:%i\r\n",candidate);
        }

        if(candidate == 0) {
            printf("Found invalid NOTE LENGTH value [%c] at position %li. Skipping until next whitespace...\r\n",*songpos,songpos-buffer);
            songpos = skipToNextEntry(songpos,0);
            i--;
            continue;
        } else {
            song[i].length = candidate;
            if(debugState) {
                printf("Entered candidate [%i] into song[%i].length\r\n",candidate,i);
            }
        }

        //parse next character
        if(debugState) {
            printf("Character:%c\r\n",*songpos);
        }

        pn_det=0;

        if(!(*songpos=='#'||'a'<=*songpos<='z'||*songpos=='-')) {
            printf("Found invalid PITCH NAME value [%c] at position %li. Skipping word...\r\n",*songpos,songpos-buffer);
            songpos = skipToNextEntry(songpos,0);
            i--;
            continue;
        }
        if(*songpos=='#') { //watch out for sharps
            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;
            case '-':
                pn_det=57;
                goto skipoctaveparse;
        }

        songpos++;

        //parse octaves
        if('0'<=*songpos<='9') {
            int num = strtol(songpos,&songpos,10);
            num+=3;//shift up thrice. board can't handle things this low.
            if(debugState) {
                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);
            songpos = skipToNextEntry(songpos,0);
            i--; //rewrite current note
            continue;
        }
skipoctaveparse:

        song[i].pitch = (pitchname)pn_det;

        if(debugState) {
            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");
        }

        songpos = skipToNextEntry(songpos,1);

        //check for buffer end
        if(*(songpos) == '\0') {
            i++;
            song[i].length = 1;
            song[i].pitch = END;
            printf("Parsing done. i = %i\r\n",i);
            return 1;
        }
        if(songpos-buffer > 2048) {
            printf("songpos exceeded buffer size.\r\n");
            return 0;
        }
    }
    printf("Song exceeded maximum number of notes.\r\n");
    return 0;
}

void printSong(note *song)
{
    for(i=0; song[i].pitch != END; i++) {
        printf("{%i,%i},",song[i].length,song[i].pitch);
    }
    printf("{%i,%i},",song[i].length,song[i].pitch);
    printf("\r\nPrinting done.\r\n");
}

int main()
{
    WavFile Track;
    Ticker SampleTime;

    daplink.printf("\f---DAPLINK SERIAL PORT---\r\n\r\nMINI PIANO PLAYER ver 2 \r\n\r\n\r\n");
    microUSB.printf("micro USB serial port\r\n");
    presence();

    //input iterator vars
    int i;
    char c;
    //input holder array
    char *buffer = new char[1024]; //ver 2 update: placed in stack.

    //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>=4096) {
            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()) {
            break;
        }
    }
    buffer[i] = ' '; //set EOF marker.
    buffer[i+1] = '\0';
    buffer[i+2] = 'a'; //make sure to put a character after (will allow skipToNextEntry() to find the null character at [songpos-1])

    //kunwari open na yung file
    printf("\r\nFile recieved. Parsing...\r\n");

    //parse file into song in heap(remove sharps, put into enums.)
    note song[1024];
    if(!songParse(song,buffer)) {
        printf("Song parse unsuccessful.\r\n");
    }

    printSong(song);

    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:

    //buffer input cycle
    for(i = 0; 1; i++) {
        //frequency select
        switch(song[i].pitch) {
            case rest:
                wait((1/(float)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) {
                    wait_ms(2);
                }
                goto restart;
        }

        //calculate ticker, attach ticker.
        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");
    }
}