Quick hack to make NSX-39 (Poke-Miku) USB MIDI device to speak "mbed" from mbed which acts as an USB host.

Dependencies:   FatFileSystem mbed

Fork of MIDI_BlueUSB by Radio Junk Box

Description of the project

This is quick hack to control Poke-miku (NSX-39) from mbed. The mbed acts as an USB host and controls USB MIDI device NSX-39. It speaks "mbed" if you send "s¥n" from virtual USB serial (connected to PC or Mac) or push SW connected to p21. It plays MIDI file "test.mid" on local file-system if you push SW connected to p22. You can find files that I have tested at the bottom. The standard MIDI file support is still preliminary. See TestShell.cpp for the hack. This program is derived from MIDI_BlueUSB (http://mbed.org/users/radiojunkbox/code/MIDI_BlueUSB/) by Radio Junk Box.

ポケミク(NSX-39)を無改造のままmbedから鳴らせるようにしてみました。mbedがUSB hostになって、USB MIDIデバイスのポケミクを鳴らします。mbedのバーチャルシリアル(USBシリアル)にPCからs\nを送るか、p21につないだスイッチを押すとmbedとしゃべります。p22につないだスイッチを押すと、ローカルファイルシステム(.binと同じ場所)に保存した test.mid を再生します。試したファイルは下にある test1.mid と test2.mid です。MIDIファイルのサポートはまだまだ完全とはいえません。

tested MIDI files

Video: Poke-miku speaks `mbed'

Files at this revision

API Documentation at this revision

Comitter:
non
Date:
Sun Apr 27 07:40:40 2014 +0000
Parent:
2:7576d1327cf1
Child:
4:cd0d8ce967d8
Commit message:
Support standard MIDI file (preliminary)

Changed in this revision

TestShell.cpp Show annotated file Show diff for this revision Revisions of this file
common.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- a/TestShell.cpp	Sun Apr 27 01:36:30 2014 +0000
+++ b/TestShell.cpp	Sun Apr 27 07:40:40 2014 +0000
@@ -1,6 +1,7 @@
 
 /*
 Copyright (c) 2010 Peter Barrett
+Copyright (c) 2014 Noriaki Mitsunaga (SMF part)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -35,7 +36,9 @@
 #include "TestShell.h"
 
 static DigitalOut myLED1(LED1);
-static DigitalIn mySW(SWpin);
+static DigitalIn mySW1(SW1pin);
+static DigitalIn mySW2(SW2pin);
+int PlaySMF(char *fname);
 
 void printf(const BD_ADDR* addr)
 {
@@ -52,9 +55,8 @@
     u8* _hciBuffer;
     u8* _aclBuffer;
 
-    public:
-    void Open(int device, u8* hciBuffer, u8* aclBuffer)
-    {
+public:
+    void Open(int device, u8* hciBuffer, u8* aclBuffer) {
         _device = device;
         _hciBuffer = hciBuffer;
         _aclBuffer = aclBuffer;
@@ -62,29 +64,25 @@
         USBBulkTransfer(_device,0x82,_aclBuffer,MAX_ACL_SIZE,AclCallback,this);
     }
 
-    static void HciCallback(int device, int endpoint, int status, u8* data, int len, void* userData)
-    {
+    static void HciCallback(int device, int endpoint, int status, u8* data, int len, void* userData) {
         HCI* t = ((HCITransportUSB*)userData)->_target;
         if (t)
             t->HCIRecv(data,len);
         USBInterruptTransfer(device,0x81,data,MAX_HCL_SIZE,HciCallback,userData);
     }
 
-    static void AclCallback(int device, int endpoint, int status, u8* data, int len, void* userData)
-    {
+    static void AclCallback(int device, int endpoint, int status, u8* data, int len, void* userData) {
         HCI* t = ((HCITransportUSB*)userData)->_target;
         if (t)
             t->ACLRecv(data,len);
         USBBulkTransfer(device,0x82,data,MAX_ACL_SIZE,AclCallback,userData);
     }
 
-    virtual void HCISend(const u8* data, int len)
-    {
+    virtual void HCISend(const u8* data, int len) {
         USBControlTransfer(_device,REQUEST_TYPE_CLASS, 0, 0, 0,(u8*)data,len);
     }
 
-    virtual void ACLSend(const u8* data, int len)
-    {
+    virtual void ACLSend(const u8* data, int len) {
         USBBulkTransfer(_device,0x02,(u8*)data,len);
     }
 };
@@ -116,8 +114,9 @@
 void Miku(u8 chr)
 {
     static u8 d[64] = {0x04, 0xf0, 0x43, 0x79,
-                       0x04, 0x09, 0x11, 0x0a, 
-                       0x07, 0x00, 0x00, 0xf7};
+                       0x04, 0x09, 0x11, 0x0a,
+                       0x07, 0x00, 0x00, 0xf7
+                      };
     d[10] = chr;
     USBBulkTransfer(NSX39_device, NSX39_endpoint, d, 12, NULL, NULL);
 }
@@ -130,22 +129,18 @@
     int _devClass;
     BD_ADDR _addr;
     u8  _pad[2];    // Struct align
-    
+
 public:
     HIDBluetooth() : _control(0),_interrupt(0),_devClass(0) {};
 
-    bool InUse()
-    {
+    bool InUse() {
         return _control != 0;
     }
 
-    static void OnHidInterrupt(int socket, SocketState state, const u8* data, int len, void* userData)
-    {
+    static void OnHidInterrupt(int socket, SocketState state, const u8* data, int len, void* userData) {
         HIDBluetooth* t = (HIDBluetooth*)userData;
-        if (data)
-        {
-            if (t->_devClass == WII_REMOTE && data[1] == 0x30)
-            {
+        if (data) {
+            if (t->_devClass == WII_REMOTE && data[1] == 0x30) {
                 printf("================wii====================\n");
                 t->Led();
                 t->Hid();   // ask for accelerometer
@@ -153,18 +148,15 @@
             }
 
             const u8* d = data;
-            switch (d[1])
-            {
-                case 0x02:
-                {
+            switch (d[1]) {
+                case 0x02: {
                     int x = (signed char)d[3];
                     int y = (signed char)d[4];
                     printf("Mouse %2X dx:%d dy:%d\n",d[2],x,y);
                 }
                 break;
 
-                case 0x37: // Accelerometer http://wiki.wiimoteproject.com/Reports
-                {
+                case 0x37: { // Accelerometer http://wiki.wiimoteproject.com/Reports
                     int pad = (d[2] & 0x9F) | ((d[3] & 0x9F) << 8);
                     int x = (d[2] & 0x60) >> 5 | d[4] << 2;
                     int y = (d[3] & 0x20) >> 4 | d[5] << 2;
@@ -178,21 +170,19 @@
         }
     }
 
-    static void OnHidControl(int socket, SocketState state, const u8* data, int len, void* userData)
-    {
+    static void OnHidControl(int socket, SocketState state, const u8* data, int len, void* userData) {
         printf("OnHidControl\n");
         if (data)
             printHex(data,len);
     }
 
-    void Open(BD_ADDR* bdAddr, inquiry_info* info)
-    {
+    void Open(BD_ADDR* bdAddr, inquiry_info* info) {
         printf("L2CAPAddr size %d\n",sizeof(L2CAPAddr));
         _addr = *bdAddr;
         L2CAPAddr sockAddr;
         sockAddr.bdaddr = _addr;
         sockAddr.psm = L2CAP_PSM_HID_INTR;
-                printf("Socket_Open size %d\n",sizeof(L2CAPAddr));
+        printf("Socket_Open size %d\n",sizeof(L2CAPAddr));
         _interrupt = Socket_Open(SOCKET_L2CAP,&sockAddr.hdr,OnHidInterrupt,this);
         sockAddr.psm = L2CAP_PSM_HID_CNTL;
         _control = Socket_Open(SOCKET_L2CAP,&sockAddr.hdr,OnHidControl,this);
@@ -201,24 +191,21 @@
         _devClass = (info->dev_class[0] << 16) | (info->dev_class[1] << 8) | info->dev_class[2];
     }
 
-    void Close()
-    {
+    void Close() {
         if (_control)
             Socket_Close(_control);
         if (_interrupt)
             Socket_Close(_interrupt);
-       _control = _interrupt = 0;
+        _control = _interrupt = 0;
     }
 
-    void Led(int id = 0x10)
-    {
+    void Led(int id = 0x10) {
         u8 led[3] = {0x52, 0x11, id};
         if (_control)
             Socket_Send(_control,led,3);
     }
 
-    void Hid(int report = 0x37)
-    {
+    void Hid(int report = 0x37) {
         u8 hid[4] = { 0x52, 0x12, 0x00, report };
         if (_control != -1)
             Socket_Send(_control,hid,4);
@@ -236,18 +223,16 @@
     HIDBluetooth    _hids[MAX_HID_DEVICES];
 
 public:
-    void Ready()
-    {
-    printf("HIDBluetooth %d\n",sizeof(HIDBluetooth));
-         memset(_hids,0,sizeof(_hids));
+    void Ready() {
+        printf("HIDBluetooth %d\n",sizeof(HIDBluetooth));
+        memset(_hids,0,sizeof(_hids));
         Inquiry();
 
     }
 
     //  We have connected to a device
-    void ConnectionComplete(HCI* hci, connection_info* info)
-    {
-    printf("ConnectionComplete ");
+    void ConnectionComplete(HCI* hci, connection_info* info) {
+        printf("ConnectionComplete ");
         BD_ADDR* a = &info->bdaddr;
         printf(a);
         BTDevice* bt = hci->Find(a);
@@ -257,23 +242,19 @@
             hid->Open(a,&bt->_info);
     }
 
-    HIDBluetooth* NewHIDBluetooth()
-    {
+    HIDBluetooth* NewHIDBluetooth() {
         for (int i = 0; i < MAX_HID_DEVICES; i++)
             if (!_hids[i].InUse())
                 return _hids+i;
         return 0;
     }
 
-    void ConnectDevices()
-    {
+    void ConnectDevices() {
         BTDevice* devs[8];
         int count = gHCI->GetDevices(devs,8);
-        for (int i = 0; i < count; i++)
-        {
+        for (int i = 0; i < count; i++) {
             printfBytes("DEVICE CLASS",devs[i]->_info.dev_class,3);
-            if (devs[i]->_handle == 0)
-            {
+            if (devs[i]->_handle == 0) {
                 BD_ADDR* bd = &devs[i]->_info.bdaddr;
                 printf("Connecting to ");
                 printf(bd);
@@ -283,11 +264,9 @@
         }
     }
 
-    const char* ReadLine()
-    {
+    const char* ReadLine() {
         int i;
-        for (i = 0; i < 255; )
-        {
+        for (i = 0; i < 255; ) {
             USBLoop();
             int c = GetConsoleChar();
             if (c == -1)
@@ -300,53 +279,48 @@
         return _line;
     }
 
-    void Inquiry()
-    {
+    void Inquiry() {
         printf("Inquiry..\n");
         gHCI->Inquiry();
     }
 
-    void List()
-    {
-        #if 0
+    void List() {
+#if 0
         printf("%d devices\n",_deviceCount);
-        for (int i = 0; i < _deviceCount; i++)
-        {
+        for (int i = 0; i < _deviceCount; i++) {
             printf(&_devices[i].info.bdaddr);
             printf("\n");
         }
-        #endif
+#endif
     }
 
-    void Connect()
-    {
+    void Connect() {
         ConnectDevices();
     }
 
-    void Disconnect()
-    {
+    void Disconnect() {
         gHCI->DisconnectAll();
     }
 
-    void CloseMouse()
-    {
+    void CloseMouse() {
     }
 
-    void Quit()
-    {
+    void Quit() {
         CloseMouse();
     }
 
-    void Run()
-    {
-        for(;;)
-        {
+    void Run() {
+        for(;;) {
             const char* cmd = "";
             USBLoop();
             if (IsConsoleReadable())
                 cmd = ReadLine();
 
-            if ((strcmp(cmd,"s") == 0) || (mySW == 0)) {
+            if (mySW2 == 0) {
+                myLED1 = 1;
+                PlaySMF("/local/test.mid");
+                myLED1 = 0;
+            } else if ((strcmp(cmd,"s") == 0) || (mySW1 == 0)) {
                 myLED1 = 1;
                 Miku(3);
                 wait(0.001);
@@ -354,14 +328,14 @@
                 wait(0.8);
                 NoteOff(0, 72, 0x7f);
                 wait(0.001);
- 
+
                 Miku(124);
                 wait(0.001);
                 NoteOn(0, 74, 0x7f);
                 wait(0.5);
                 NoteOff(0, 74, 0x7f);
                 wait(0.001);
- 
+
                 Miku(79);
                 wait(0.001);
                 NoteOn(0, 76, 0x7f);
@@ -376,8 +350,7 @@
                 wait(0.5);
                 NoteOff(0, 76, 0x7f);
                 myLED1 = 0;
-            } else 
-            if (strcmp(cmd,"scan") == 0 || strcmp(cmd,"inquiry") == 0)
+            } else if (strcmp(cmd,"scan") == 0 || strcmp(cmd,"inquiry") == 0)
                 Inquiry();
             else if (strcmp(cmd,"ls") == 0)
                 List();
@@ -385,8 +358,7 @@
                 Connect();
             else if (strcmp(cmd,"disconnect") == 0)
                 Disconnect();
-            else if (strcmp(cmd,"q")== 0)
-            {
+            else if (strcmp(cmd,"q")== 0) {
                 Quit();
                 break;
             } else if (*cmd == 0) {
@@ -403,8 +375,7 @@
 
 static int HciCallback(HCI* hci, HCI_CALLBACK_EVENT evt, const u8* data, int len)
 {
-    switch (evt)
-    {
+    switch (evt) {
         case CALLBACK_READY:
             printf("CALLBACK_READY\n");
             gApp.Ready();
@@ -421,14 +392,13 @@
             gApp.ConnectDevices();
             break;
 
-        case CALLBACK_REMOTE_NAME:
-            {
-                BD_ADDR* addr = (BD_ADDR*)data;
-                const char* name = (const char*)(data + 6);
-                printf(addr);
-                printf(" % s\n",name);
-            }
-            break;
+        case CALLBACK_REMOTE_NAME: {
+            BD_ADDR* addr = (BD_ADDR*)data;
+            const char* name = (const char*)(data + 6);
+            printf(addr);
+            printf(" % s\n",name);
+        }
+        break;
 
         case CALLBACK_CONNECTION_COMPLETE:
             gApp.ConnectionComplete(hci,(connection_info*)data);
@@ -438,8 +408,7 @@
 }
 
 //  these should be placed in the DMA SRAM
-typedef struct
-{
+typedef struct {
     u8 _hciBuffer[MAX_HCL_SIZE];
     u8 _aclBuffer[MAX_ACL_SIZE];
 } SRAMPlacement;
@@ -468,3 +437,428 @@
     USBInit();
     gApp.Run();
 }
+
+/* For Handling Standard Midi File */
+struct midiPacket {
+    unsigned short delta;
+    unsigned short len;
+    unsigned char *p;
+};
+
+class midiHeader
+{
+private:
+    char magic[4];
+    unsigned long sz;
+public:
+    unsigned short fmt;
+    unsigned short tracks;
+    unsigned short delta;
+
+    void readFile(FILE *fp);
+    int check();
+};
+
+class midiTrack
+{
+private:
+    char magic[4];
+    unsigned char run_status;
+    unsigned char last_len;
+    unsigned char buf[128];
+public:
+    unsigned long tempo;
+    unsigned long sz;
+    unsigned char *dat;
+    unsigned char *cur;
+
+    midiTrack() {
+        tempo = 1000*1000L;
+        dat = NULL;
+    }
+    ~midiTrack() {
+        if (dat != NULL) delete dat;
+    }
+    void dump();
+    bool isEnd() {
+        if (cur < (dat + sz)) return false;
+        return true;
+    }
+    struct midiPacket next();
+    void readFile(FILE *fp);
+    void rewind() {
+        cur = dat;
+    }
+};
+
+unsigned long read32(FILE *fp);
+unsigned short read16(FILE *fp);
+
+/* ------------------------- Functions ---------------------- */
+unsigned long read32(FILE *fp)
+{
+    int i;
+    unsigned long ret = 0;
+
+    for (i = 0, ret = 0; i < 4; i ++) {
+        ret = (ret << 8) | (unsigned char)fgetc(fp);
+    }
+    return ret;
+}
+
+unsigned short read16(FILE *fp)
+{
+    int i;
+    unsigned short ret = 0;
+
+    for (i = 0, ret = 0; i < 2; i ++) {
+        ret = (ret << 8) | (unsigned char)fgetc(fp);
+    }
+    return ret;
+}
+
+int midiHeader::check()
+{
+    if (strncmp(magic, "MThd", 4) != 0 || sz != 6) {
+        return 1;
+    }
+    return 0;
+}
+
+void midiHeader::readFile(FILE *fp)
+{
+    fread(magic, 4, 1, fp);
+    sz = read32(fp);
+    fmt = read16(fp);
+    tracks = read16(fp);
+    delta = read16(fp);
+}
+
+void midiTrack::dump()
+{
+    unsigned char *p = dat, *e = dat + sz;
+    unsigned long delta;
+
+    while (p < e) {
+        delta = 0;
+        while ((*p & 0x80) != 0) {
+            delta = (delta << 7) | (*p & 0x7f);
+            p ++;
+        }
+        delta = (delta << 7) | *p;
+        p ++;
+
+        fprintf(stderr, "%lu: ", delta);
+
+        unsigned char evt = (*p & 0xf0);
+        switch (evt) {
+            case 0xd0:
+                /* 2bytes events */
+                fprintf(stderr, "%02x %02x\n", *p, *(p+1));
+                p += 2;
+                last_len = 2;
+                break;
+
+            case 0x80:
+            case 0x90:
+            case 0xa0:
+            case 0xe0:
+                /* 3bytes events */
+                fprintf(stderr, "%02x %02x %02x \n", *p, *(p+1), *(p+2));
+                p += 3;
+                last_len = 3;
+                break;
+
+            case 0xb0:
+                /* 3-4bytes events */
+                if (*(p+1) == 0x7e && *(p+2) == 0x00 && *(p+3) == 0x04) {
+                    /* 4bytes */
+                    fprintf(stderr, "%02x %02x %02x %02x \n", *p, *(p+1), *(p+2), *(p+3));
+                    p += 4;
+                } else {
+                    /* 3bytes events */
+                    fprintf(stderr, "%02x %02x %02x \n", *p, *(p+1), *(p+2));
+                    p += 3;
+                }
+                last_len = 0;
+                break;
+
+            case 0xf0:
+                if (*p == 0xff) {
+                    /* meta events */
+                    if (*(p+1) == 0x51 && *(p+2) == 0x03) {
+                        tempo = (*(p+3)<<16) | (*(p+4)<<8) | *(p+5);
+                        fprintf(stderr, "(Tempo: %lu)", tempo);
+                    }
+                    fprintf(stderr, "%02x %02x %02x ", *p, *(p+1), *(p+2));
+                    unsigned char len = *(p+2);
+                    p += 3;
+                    for (int i=0; i<len; i++) {
+                        fprintf(stderr, "%02x ", *(p++));
+                    }
+                    fprintf(stderr, "\n");
+                } else {
+                    /* SysEx event (variable length) */
+                    fprintf(stderr, "%02x %02x ", *p, *(p+1));
+                    unsigned char len = *(p+1);
+                    p += 2;
+                    for (int i=0; i<len; i++) {
+                        fprintf(stderr, "%02x ", *(p++));
+                    }
+                    fprintf(stderr, "\n");
+                }
+                last_len = 0;
+                break;
+
+            default:
+                /* running status */
+                for (int i=1; i<last_len; i++) {
+                    fprintf(stderr, "%02x ", *(p++));
+                }
+                fprintf(stderr, "\n");
+                break;
+        }
+    }
+}
+
+struct midiPacket midiTrack::next() {
+    struct midiPacket ret;
+    unsigned char *p = cur, *e = dat + sz, *q = buf;
+    unsigned long delta;
+
+    ret.len = 0;
+    if (cur >= dat + sz)
+        return ret;
+
+    delta = 0;
+    while ((*p & 0x80) != 0) {
+        delta = (delta << 7) | (*p & 0x7f);
+        p ++;
+    }
+    delta = (delta << 7) | *p;
+    p ++;
+    ret.delta = delta;
+    ret.p = buf;
+
+    while (p < e) {
+        unsigned char evt = (*p & 0xf0);
+        switch (evt) {
+            case 0xd0:
+                /* 2bytes events */
+                run_status = *p;
+                *(q++) = evt >> 4 | (0x00);
+                memcpy(q, p, 2);
+                p += 2;
+                q += 2;
+                *(q++) = 0;
+                ret.len += 4;
+                last_len = 2;
+                break;
+
+            case 0x80:
+            case 0x90:
+            case 0xa0:
+            case 0xe0:
+                run_status = *p;
+                *(q++) = evt >> 4 | (0x00);
+                memcpy(q, p, 3);
+                p += 3;
+                q += 3;
+                ret.len += 4;
+                last_len = 3;
+                break;
+
+            case 0xb0:
+                run_status = *p;
+                *(q++) = evt >> 4 | (0x00);
+                /* 3-4bytes events */
+                if (*(p+1) == 0x7e && *(p+2) == 0x00 && *(p+3) == 0x04) {
+                    /* 4bytes */
+                    memcpy(q, p, 4);
+                    p += 4;
+                    q += 4;
+                    ret.len += 5;
+                } else {
+                    /* 3bytes events */
+                    memcpy(q, p, 3);
+                    p += 3;
+                    q += 3;
+                    ret.len += 4;
+                }
+                last_len = 0;
+                break;
+
+            case 0xf0: {
+                unsigned char len;
+                if (*p == 0xff) {
+                    /* meta events */
+                    if (*(p+1) == 0x51 && *(p+2) == 0x03) {
+                        tempo = (*(p+3)<<16) | (*(p+4)<<8) | *(p+5);
+                    }
+                    len = *(p+2) + 3;
+#if 0
+                    memcpy(q, p, len);
+                    p += len;
+                    q += len;
+                    ret.len += len;
+#else
+                    /* just ignore */
+                    p += len;
+#endif
+                } else {
+                    /* SysEx event (variable length) */
+                    len = *(p+1) + 1;
+                    unsigned char buf_[127], *s;
+
+                    buf_[0] = *p;
+                    memcpy(buf_+1, p+2, len);
+                    s = buf_;
+                    p += len + 1;
+
+                    while (len > 3) {
+                        *(q++) = 0x4 | 0x00;
+                        memcpy(q, s, 3);
+                        q += 3;
+                        s += 3;
+                        ret.len += 4;
+                        len -= 3;
+                    }
+                    switch (len) {
+                        case 3:
+                            *(q++) = 0x7 | 0x00;
+                            memcpy(q, s, 3);
+                            q += 3;
+                            ret.len += 4;
+                            break;
+                        case 2:
+                            *(q++) = 0x6 | 0x00;
+                            memcpy(q, s, 2);
+                            q += 2;
+                            *(q++) = 0;
+                            ret.len += 4;
+                            break;
+                        case 1:
+                            *(q++) = 0x5 | 0x00;
+                            memcpy(q, s, 1);
+                            q += 1;
+                            *(q++) = 0;
+                            *(q++) = 0;
+                            ret.len += 4;
+                            break;
+                    }
+                }
+            }
+            last_len = 0;
+            break;
+
+            default:
+                /* running status */
+                *(q++) = run_status >> 4;
+                *(q++) =                 run_status;
+                memcpy(q, p, last_len);
+                p += last_len;
+                q += last_len;
+                ret.len += (last_len+2);
+        }
+        if (*p != 0) /* Breaks if delta is not 0 */
+            break;
+        p ++;
+    }
+    cur = p;
+
+    return ret;
+}
+
+void midiTrack::readFile(FILE *fp)
+{
+    fread(magic, 4, 1, fp);
+    sz = read32(fp);
+    dat = new unsigned char[sz];
+    fread(dat, 1, sz, fp);
+    cur = dat;
+}
+
+int PlaySMF(char *fname)
+{
+    FILE *fp;
+
+    if ((fp = fopen(fname, "r")) == NULL) {
+        fprintf(stderr, "Could not open file %s.\n", fname);
+        return 1;
+    }
+
+    struct midiHeader *mh = new midiHeader;
+    mh->readFile(fp);
+
+    if (mh->check() != 0) {
+        fprintf(stderr, "Not a MIDI file.\n");
+        delete mh;
+        fclose(fp);
+        return 1;
+    }
+
+    midiTrack* trks[32] = {NULL};
+
+    for (int i=0; i<mh->tracks; i++) {
+        trks[i] = new midiTrack;
+        trks[i]->readFile(fp);
+    }
+    fclose(fp);
+
+    for (int i=0; i<mh->tracks; i++) {
+        if (trks[i] == NULL)
+            continue;
+        trks[i]->rewind();
+    }
+
+    for (;;) {
+        struct midiPacket pkt[32];
+        bool songEnd = true;
+
+        for (int i=0; i<mh->tracks; i++) {
+            pkt[i].len = 0;
+
+            if (trks[i] == NULL)
+                continue;
+
+            if (!trks[i]->isEnd()) {
+                pkt[i] = trks[i]->next();
+                songEnd = false;
+            }
+        }
+        if (songEnd)
+            break;
+
+        for (int t=0; ; t += 10) {
+            USBLoop();
+            bool toNext = true;
+            for (int i=0; i<mh->tracks; i++) {
+                if (pkt[i].len == 0) {
+                    continue;
+                }
+                toNext = false;
+                if (t >= pkt[i].delta) {
+                    fprintf(stderr, "%d[%d] ", t, i);
+                    for (int j=0; j<pkt[i].len; j++) {
+                        fprintf(stderr, "%02x ", pkt[i].p[j]);
+                    }
+                    fprintf(stderr, "\n");
+                    USBBulkTransfer(NSX39_device, NSX39_endpoint,
+                                    pkt[i].p, pkt[i].len, NULL, NULL);
+                    pkt[i].len = 0;
+                }
+            }
+            if (toNext)
+                break;
+//    fprintf(stderr, "%f ", (float)trks[0]->tempo/(float)mh->delta/1000000.0);
+            wait((float)trks[0]->tempo/(float)mh->delta/100000.0);
+        }
+    }
+
+    for (int i=0; i<mh->tracks; i++) {
+        delete trks[i];
+    }
+    delete mh;
+
+    return 0;
+}
--- a/common.h	Sun Apr 27 01:36:30 2014 +0000
+++ b/common.h	Sun Apr 27 07:40:40 2014 +0000
@@ -1,7 +1,8 @@
 #ifndef __COMMON_H__
 #define __COMMON_H__
 
-#define SWpin p21
+#define SW1pin p21  // Say "mbed"
+#define SW2pin p22  // Play "test.mid" file on local filesystem
 
 extern int GetConsoleChar();
 extern bool IsConsoleReadable();
--- a/main.cpp	Sun Apr 27 01:36:30 2014 +0000
+++ b/main.cpp	Sun Apr 27 07:40:40 2014 +0000
@@ -124,15 +124,20 @@
 }
 
 
-static DigitalIn mySW(SWpin);
+static DigitalIn mySW1(SW1pin);
+static DigitalIn mySW2(SW2pin);
 static DigitalOut myLED(LED1);
+LocalFileSystem local("local"); // Need this if you want to save to local flash
 
 int main()
 {
 //    pc.baud(460800);
+    mySW1.mode(PullUp);
+    mySW2.mode(PullUp);
+
     printf("BlueUSB\nNow get a bunch of usb or bluetooth things and plug them in\n");
     InitSerialMIDI();   // Added by RadioJunkBox
-    if (mySW.read() == 0) {
+    if (mySW1 == 0) {
         myLED = 1;
         wait(0.5);
         myLED = 0;