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
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'
Diff: TestShell.cpp
- Revision:
- 3:31fbce33c25b
- Parent:
- 2:7576d1327cf1
- Child:
- 4:cd0d8ce967d8
--- 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; +}