Standard MIDI file player for the eVY1 shield and analog joy stick

Dependencies:   DirectoryList SDFileSystem mbed

Fork of eVY1_SMF_player by Toyomasa Watarai

MicroSDカードからSMF(スタンダードMIDIファイル)を読み込み、データをシリアルでeVY1シールドに転送して再生します。 MIDIファイル形式は、Format 0のみ対応しています。アナログジョイスティックを使用してテンポとピッチの変更が出来るようにしました。

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

eVY1シールドをそのまま刺して使用できます(オンボードのMicroSDスロットを使います)。

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

アナログジョイスティックの X Y データは、それぞれアナログ入力の A0, A1 に接続しています。

main.cpp

Committer:
MACRUM
Date:
2017-01-07
Revision:
4:4dcc1464c89e
Parent:
3:2a58b7f4b0cb

File content as of revision 4:4dcc1464c89e:

/**
 *  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);
AnalogIn ax(A0);
AnalogIn ay(A1);
Ticker click;
float x, y;

#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, org_tempo;
uint32_t delta_time;
uint32_t TIMER;
uint32_t STATE;
uint32_t pitch_update = 0;

#define midi_read() (fgetc(fp))

void get_val(void)
{
    x = ax.read();
    y = ay.read();
    tempo = org_tempo + ((ax - 0.5f) * 400);
    pitch_update = 1;
}

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;

    if (pitch_update) {
        uint32_t pitch;
        pitch = 0x4000 + ((y - 0.5f) * 2000);
        for(uint32_t i=0; i<16; i++) {
            midi.putc(0xE0 | i);
            midi.putc(pitch & 0x7F);
            midi.putc((pitch >> 8) & 0x7F);
        }
        pitch_update = 0;
    }
    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;
                        org_tempo = tempo;
                        //tempo += (ax * 10);
                        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:
                    case 0x03:
                    case 0x04:
                    case 0x05:
                        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);

    click.attach(&get_val, 0.2);

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