Standard MIDI file player for the eVY1 shield and analog joy stick
Dependencies: DirectoryList SDFileSystem mbed
Fork of eVY1_SMF_player by
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 に接続しています。
Diff: main.cpp
- Revision:
- 0:12d69da08021
- Child:
- 1:c536df09d2e8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sat Jul 18 01:40:09 2015 +0000 @@ -0,0 +1,247 @@ +#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); + +#endif + +#ifdef _DEBUG +Serial pc(USBTX, USBRX); +#define DEBUG_PRINT(...) { pc.printf(__VA_ARGS__);} +#else +#define DEBUG_PRINT(...) +#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 + DEBUG_PRINT("*"); + 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; + 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 + +#if defined(_DEBUG) + // 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 != 0) + DEBUG_PRINT("This is not MID format 0 file!\n"); + + uint32_t track; + track = (midi_read() << 8); + track |= midi_read(); + + if (track != 1) + DEBUG_PRINT("Number of tracks should be 1!\n"); +#else + // Skip MIDI header + for (uint32_t i=0; i<12; i++) { + midi_read(); + } +#endif + + // 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.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(); + 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"); + } + } +}