Standard MIDI file player for the eVY1 shield

Dependencies:   DirectoryList SDFileSystem mbed

MicroSDカードからSMF(スタンダードMIDIファイル)を読み込み、データをシリアルでeVY1シールドに転送して再生します。 MIDIファイル形式は、Format 0のみ対応しています。

動作確認は、mbed LPC1114FN28とFRDM-K64Fで行っています。

eVY1でシリアルポートを有効にするには、シールドの端子側から+5V入力が必要なので、mbed LPC1114FN28を一部改造しています(JP2 9pinに+5V出力を接続しています)。 FRDM-K64Fの場合は、eVY1シールドをそのまま刺して使用できます(オンボードのMicroSDスロットを使います)。

eVY1を使用した場合、MIDIデータのCH.1は強制的にeVocalodによる歌声として使用されてしまうため(プログラムチェンジも不可)、強制的にCH.16に割り当てています。そのため、CH.16を使用しているMIDIファイルはデータ通りに再生する事が出来ません。

main.cpp

Committer:
MACRUM
Date:
2017-01-06
Revision:
3:2a58b7f4b0cb
Parent:
2:4bcf9c18896b

File content as of revision 3:2a58b7f4b0cb:

/**
 *  Standard MIDI file player for the eVY1 shield
 *
 *  @author  Toyomasa Watarai
 *  @version 0.1
 *  @date    July-2015
 *
 *  This program parse MIDI format 0 files on SDCard
 *  and plays the data using eVY1 shield
 *
 *  Ported by Arduino example code of the SWITCHSCIENCE, Thanks!
 *  https://github.com/SWITCHSCIENCE/eVY1_Shield
 *
 */

#include "mbed.h"
#include "SDFileSystem.h"
#include "DirectoryList.h"

#define _DEBUG
#define _NO_eVocaloid_

#if defined(TARGET_K64F)
#define USE_DIRECTORY_LIST
SDFileSystem sd(PTE3, PTE1, PTE2, PTE4, "sd"); // MOSI, MISO, SCK, CS
RawSerial midi(D1, NC);
InterruptIn btn(PTA4);

#elif defined(TARGET_LPC1114)
SDFileSystem sd(dp2, dp1, dp6, dp4, "sd"); // MOSI, MISO, SCK, CS
RawSerial midi(dp16, NC);
InterruptIn btn(dp9);
#if defined(_DEBUG)
#undef _DEBUG
#endif

#elif defined(TARGET_WIZWIKI_W7500P)
SDFileSystem sd(PB_3, PB_2, PB_1, PB_0, "sd"); // MOSI, MISO, SCK, CS
RawSerial midi(D1, NC);
InterruptIn btn(PC_6);
#if defined(_DEBUG)
#undef _DEBUG
#endif

#endif

#ifdef _DEBUG
Serial pc(USBTX, USBRX);
#define DEBUG_PRINT(...)    { pc.printf(__VA_ARGS__);}
#else
#define DEBUG_PRINT(...)
void error(const char* format, ...) {}
#endif

FILE *fp;
Timer timer;
uint32_t tempo;
uint32_t delta_time;
uint32_t TIMER;
uint32_t STATE;

#define midi_read() (fgetc(fp))

void disable_timer(void)
{
    timer.stop();
}

uint32_t delta_time_read(void)
{
    uint32_t r_buf;
    uint32_t ret = 0;

    while(1) {
        r_buf = midi_read();
        ret = (ret <<7) | (r_buf & 0x7f);
        if ((r_buf & 0x80) == 0)
            break;
    }

    TIMER += ((ret * tempo) / delta_time);
    return ret;
}

void midi_play(void)
{
    int32_t  buf[256];
    uint32_t cnt;
    uint32_t cmd;

    buf[0] = midi_read();
    buf[1] = midi_read();

    cmd = (buf[0] & 0xf0);
    if ((cmd == 0x80) || (cmd == 0x90) || (cmd == 0xA0) || (cmd == 0xB0) || (cmd == 0xE0)) {
        buf[2] = midi_read();
#if defined(_NO_eVocaloid_)
        if ((buf[0] & 0x0f) == 0x0f) {    // CH.16
            return;
        }
        if ((buf[0] & 0x0f) == 0x00) {    // CH.1
            buf[0] = (buf[0] | 0x0f);     // Force change to CH.16
        }
#endif
        midi.putc(buf[0]);
        midi.putc(buf[1]);
        midi.putc(buf[2]);
    } else if (cmd == 0xC0) {
#if defined(_NO_eVocaloid_)
        if ((buf[0] & 0x0f) == 0x00) {    // CH.1
            buf[0] = (buf[0] | 0x0f);     // Force change to CH.16
        }
#endif
        midi.putc(buf[0]);
        midi.putc(buf[1]);
    } else if (cmd == 0xD0) {
        midi.putc(buf[0]);
        midi.putc(buf[1]);
    } else if (cmd == 0xF0) {
        switch( buf[0] & 0x0F ) {
            case 0x00 : // SysEx
            case 0x07 : // SysEx2
                cnt = buf[1];
                midi.putc(buf[0]);
                for(uint32_t i=1; i<cnt+1; i++) {
                    midi.putc(midi_read());
                }
                break;
            case 0x0f : // Meta event
                switch ( buf[1] ) {
                    case 0x00: // Sequence number
                        midi_read();
                        break;
                    case 0x51: // Set tempo
                        midi_read(); // len (== 3)
                        tempo = midi_read();
                        tempo = (tempo << 8 ) | midi_read();
                        tempo = (tempo << 8 ) | midi_read();
                        tempo = tempo / 1000;
                        DEBUG_PRINT("Set tempo = %d\n", tempo);
                        break;
                    case 0x2f: // End of Track
                        midi_read(); // Read zero
                        disable_timer();
                        STATE = 2;
                        break;
                    case 0x01:
                    case 0x02:
                        cnt = midi_read(); // len
                        for(uint32_t i=0; i<cnt; i++)
                            DEBUG_PRINT("%c", midi_read());
                        DEBUG_PRINT("\n");
                        break;
                    default:
                        cnt = midi_read(); // len
                        for(uint32_t i=0; i<cnt; i++)
                            midi_read();
                        break;
                }
                break;
        }
    }
}


void smf_main_loop(void)
{
    if(STATE == 1) {
        if (TIMER < timer.read_ms()) {
            midi_play();
            if (STATE != 2)
                delta_time_read();
        }
    }
}

void smf_init(void)
{
    TIMER = 0;
    tempo = 500; // default value

    uint32_t ch;
    for (ch=0; ch<16; ch++) {
        midi.putc(0xB0|ch);
        midi.putc(0x78);
        midi.putc(0x00);
        midi.putc(0xB0|ch);
        midi.putc(0x79);
        midi.putc(0x00);
    }

    // Skip MIDI header
    for (uint32_t i=0; i<8; i++) {
        midi_read();
    }

    uint32_t format;
    format = (midi_read() << 8);
    format |= midi_read();

    if ( format > 1) {
        DEBUG_PRINT("This is not a MIDI format 0 file!\n", format);
        STATE = 2;
        return;
    }

    uint32_t track;
    track = (midi_read() << 8);
    track |= midi_read();

    DEBUG_PRINT("Number of tracks : %d\n", track);

    // timebase
    delta_time = (midi_read() << 8);
    delta_time |= midi_read();
    DEBUG_PRINT("tempo = %d, delta_time = %d\n", tempo, delta_time);

    // skip track chunk header
    for (uint32_t i=0; i<0x8; i++)
        midi_read();

    TIMER = (delta_time_read() * tempo) / delta_time ;
    DEBUG_PRINT("TIMER = %d\n", TIMER);
    STATE = 1;
}

void skip()
{
    STATE = 2;
}

int main()
{
    DEBUG_PRINT("Initializing...\n");

    btn.mode(PullUp);
    btn.fall(&skip);
    midi.baud(31250);

    wait(3.5);    // Wait few seconds for booting eVY1-Shleld.

#if !defined(_NO_eVocaloid_)
    const uint8_t aMsg[] = "\xF0\x43\x79\x09\x00\x50\x10" "4 a\0" "\xF7";
    for (uint32_t i = 0; i < sizeof(aMsg)-1; midi.putc(aMsg[i++]));
#endif

    DEBUG_PRINT("Initialized.\n");

    char buf[50];

#if defined(USE_DIRECTORY_LIST)
    DirectoryList   dir("/sd");
    if ( dir.error_check() ) {
        DEBUG_PRINT("directory could not be opened\r\n");
        return -1;
    }

    for ( int i = 0; i < dir.size(); i++ ) {
        sprintf(buf, "/sd/%s", dir[ i ].c_str() );
#else
    for ( uint32_t i = 0; i < 10; i++ ) {
        sprintf(buf, "/sd/%d.mid", i);
#endif

        fp = fopen(buf, "r");
        if (fp == NULL) {
            DEBUG_PRINT("Unable to read the file \n");
        } else {
            timer.reset();
            timer.start();
            smf_init();
            if ( STATE == 2 ) {
                fclose(fp);
#if defined(__MICROLIB) && defined(__ARMCC_VERSION) // with microlib and ARM compiler
                free(fp);
#endif
                continue;
            }

            DEBUG_PRINT("Now, playing (%s)... \n", buf);
            while (1) {
                smf_main_loop();
                if (STATE == 2) {
                    break;
                }
            }
            fclose(fp);
#if defined(__MICROLIB) && defined(__ARMCC_VERSION) // with microlib and ARM compiler
            free(fp);
#endif
            DEBUG_PRINT("End.\n");
        }
    }
}