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:
- 2015-07-20
- Revision:
- 1:c536df09d2e8
- Parent:
- 0:12d69da08021
- Child:
- 2:4bcf9c18896b
File content as of revision 1:c536df09d2e8:
/** * 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 #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 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 // 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 > 2) { DEBUG_PRINT("This is not a MIDI format file!\n", format); STATE = 2; return; } else { DEBUG_PRINT("MIDI format : %d\n", format); } 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"); } } }