Lawrence Quizon / Mbed OS mini_piano_player

Dependencies:   USBMSD_BD SDFileSystem max32630fthr USBDevice

main.cpp

Committer:
Lugs
Date:
2019-07-23
Revision:
9:17de551d2208
Parent:
8:ce16aa4cdb6a
Child:
10:97f600e6eae2

File content as of revision 9:17de551d2208:

#include "mbed.h"
#include "max32630fthr.h"
#include "USBSerial.h"
#include "stdio.h"
#include "SDFileSystem.h"
#include "ctype.h"
#include "USBMSD_BD.h"
#include "SDBlockDevice.h"
#include "HeapBlockDevice.h"
#include "FATFileSystem.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];

// Physical block device, can be any device that supports the BlockDevice API
// HeapBlockDevice bd(512*BLOCK_SIZE, BLOCK_SIZE);
SDBlockDevice bd(P0_5, P0_6, P0_4, P0_7);

// File system declaration
FATFileSystem fs("fs");

// USB MSD 
USBMSD_BD msd(&bd);  


// main() runs in its own thread in the OS
// (note the calls to Thread::wait below for delays)
void startFileSystem()
{
    printf("\f---STARTING FILESYSTEM...---\r\n");
    rLED = LED_ON;
    gLED = LED_ON;
    bLED = LED_OFF;

    Thread::wait(100);

    // Try to mount the filesystem
    printf("Mounting the filesystem... ");
    fflush(stdout);
    int err = fs.mount(&bd);
    printf("%s\r\n", (err ? "Fail :(" : "OK"));
    if (err) {
        // Reformat if we can't mount the filesystem
        // this should only happen on the first boot
        printf("No filesystem found, formatting... ");
        fflush(stdout);
        err = fs.reformat(&bd);
        printf("%s\r\n", (err ? "Fail :(" : "OK"));
    }

    rLED = LED_OFF;

    // Open the numbers file
    printf("Opening \"/fs/numbers.txt\"... ");
    fflush(stdout);
    FILE *f = fopen("/fs/numbers.txt", "r+");
    printf("%s\r\n", (!f ? "Fail :(" : "OK"));
    if (!f) {
        // Create the numbers file if it doesn't exist
        printf("No file found, creating a new file... ");
        fflush(stdout);
        f = fopen("/fs/numbers.txt", "w+");
        printf("%s\r\n", (!f ? "Fail :(" : "OK"));

        for (int i = 0; i < 10; i++) {
            printf("\rWriting numbers (%d/%d)... ", i, 10);
            fflush(stdout);
            err = fprintf(f, "    %d\r\n", i);
            if (err < 0) {
                printf("Fail :(\r\n");
            }
        }
        printf("\rWriting numbers (%d/%d)... OK\r\n", 10, 10);

        printf("Seeking file... ");
        fflush(stdout);
        err = fseek(f, 0, SEEK_SET);
        printf("%s\r\n", (err < 0 ? "Fail :(" : "OK"));
    }

    // Go through and increment the numbers
    for (int i = 0; i < 10; i++) {
        printf("\rIncrementing numbers (%d/%d)... ", i, 10);
        fflush(stdout);

        // Get current stream position
        long pos = ftell(f);

        // Parse out the number and increment
        int32_t number;
        fscanf(f, "%d", &number);
        number += 1;

        // Seek to beginning of number
        fseek(f, pos, SEEK_SET);
    
        // Store number
        fprintf(f, "    %d\r\n", number);
    }
    printf("\rIncrementing numbers (%d/%d)... OK\r\n", 10, 10);

    // Close the file which also flushes any cached writes
    printf("Closing \"/fs/numbers.txt\"... ");
    fflush(stdout);
    err = fclose(f);
    printf("%s\r\n", (err < 0 ? "Fail :(" : "OK"));

    // Display the root directory
    printf("Opening the root directory... ");
    fflush(stdout);
    DIR *d = opendir("/fs/");
    printf("%s\r\n", (!d ? "Fail :(" : "OK"));

    printf("root directory:\r\n");
    while (true) {
        struct dirent *e = readdir(d);
        if (!e) {
            break;
        }
        printf("    %s\r\n", e->d_name);
    }

    printf("Closing the root directory... ");
    fflush(stdout);
    err = closedir(d);
    printf("%s\r\n", (err < 0 ? "Fail :(" : "OK"));

    // Display the numbers file
    printf("Opening \"/fs/numbers.txt\"... ");
    fflush(stdout);
    f = fopen("/fs/numbers.txt", "r");
    printf("%s\r\n", (!f ? "Fail :(" : "OK"));

    printf("numbers:\r\n");
    while (!feof(f)) {
        int c = fgetc(f);
        printf("%c", c);
    }

    printf("\rClosing \"/fs/numbers.txt\"... ");
    fflush(stdout);
    err = fclose(f);
    printf("%s\r\n", (err < 0 ? "Fail :(" : "OK"));




    // Switch to MSD
//    printf("Unmounting... ");
//    fflush(stdout);
//    err = fs.unmount();
//    printf("%s\r\n", (err < 0 ? "Fail :(" : "OK"));
    

    printf("Starting MSD... ");
    msd.disk_initialize();
    err = msd.connect();
    bLED = LED_ON;
    printf("%s\r\n", (err < 0 ? "Fail :(" : "OK"));
}

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;

    startFileSystem();

    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 arrays
    char *buffer = new char[1024]; //ver 2 update: placed in stack.
    
    FILE *txtfile = fopen("/fs/furelise.txt","r");
    
    //find file size
    fseek(txtfile,0L,SEEK_END);
    int size = ftell(txtfile);
    fseek(txtfile,0,SEEK_SET);
    
    if(txtfile == NULL)
    {
        printf("File not found.\r\n");
        while(1)
        {
            bLED = 0;
            rLED = !rLED;
            wait_ms(500);
        }
    }

    fread(buffer,1,size,txtfile);
    
    printf("Printing file...\r\n");
    for(i=0;i<size;i++)
    {
        printf("%c",buffer[i]);
    }
    
    //open file
    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");
        return 0;
    }

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