Skip to content

Instantly share code, notes, and snippets.

@mmalex
Created June 30, 2017 13:27
Show Gist options
  • Select an option

  • Save mmalex/c5b61f824d55cf6dff8a53b451af3392 to your computer and use it in GitHub Desktop.

Select an option

Save mmalex/c5b61f824d55cf6dff8a53b451af3392 to your computer and use it in GitHub Desktop.

Revisions

  1. Alex Evans created this gist Jun 30, 2017.
    120 changes: 120 additions & 0 deletions midi2osc.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,120 @@
    /*
    quick midi 2 osc hack by @mmalex, use at your own risk etc
    reads midi messages, forward them to osc messages on your network.
    barely tested but works for me in windows 7, compiled with msvc 2015 sp2
    stick a midi2osc.ini file in the same folder as the exe to configure it, with lines like these:
    IP=43.193.207.78
    Port=9000
    Log=1
    Device=0
    Device sets the midi device number (as enumerated by windows, sorry).
    Log dumps midi messages to console window
    IP and Port say where to send the osc messages, which are all just addressed to /midi with a single integer parameter with the 3 byte standard midi message packed into it.
    */
    #define _WINSOCK_DEPRECATED_NO_WARNINGS
    #define _CRT_SECURE_NO_WARNINGS
    #include <winsock2.h> // for sockets
    #include <conio.h>// for _getch
    #include <stdio.h> // for printf
    #include <ws2tcpip.h> // for getaddrinfo
    #pragma comment(lib,"winmm.lib") // for midi
    #pragma comment(lib,"ws2_32.lib") // for sockets

    const char *GetIniSetting(const char *key, const char *default_value); // simple ini file parser (see the end of this file)

    int log=0;
    SOCKET fd;
    struct addrinfo* addr=0;

    void CALLBACK midicb( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
    if (wMsg==MIM_DATA) {
    char oscmsg[16]="/midi\0\0\0,i\0\0\0\0\0";
    oscmsg[15]=(unsigned char)dwParam1;
    oscmsg[14]=(unsigned char)(dwParam1>>8);
    oscmsg[13]=(unsigned char)(dwParam1>>16);
    if (log) printf("%02x %02x %02x\r",oscmsg[15],oscmsg[14],oscmsg[13]);
    if (sendto(fd,oscmsg,16,0,addr->ai_addr,addr->ai_addrlen)<0)
    printf("failed to send udp packet!\n");
    }
    }

    int main(int argc, char **argv) {
    WSADATA wsadata={};
    WSAStartup(MAKEWORD(2,2),&wsadata);
    const char *ip=GetIniSetting("IP","127.0.0.1");
    const char *port=GetIniSetting("Port","9000");
    UINT devid=atoi(GetIniSetting("Device","0"));
    log=atoi(GetIniSetting("Log","0"));
    printf("midi2osc - sending to %s:%s, logging=%d, device=%d\npress escape to quit\n",ip,port,log,devid);

    struct addrinfo hints={};
    hints.ai_family=AF_UNSPEC;
    hints.ai_socktype=SOCK_DGRAM;
    hints.ai_protocol=0;
    hints.ai_flags=AI_ADDRCONFIG;

    if (getaddrinfo(ip,port,&hints,&addr)!=0) return printf("failed to resolve remote socket address");
    fd=socket(addr->ai_family, addr->ai_socktype,addr->ai_protocol);
    if (fd<0) return printf("failed to open socket %s\n",strerror(errno));

    UINT numdevs=midiInGetNumDevs();
    for (UINT d=0;d<numdevs;++d) {
    MIDIINCAPS caps;
    if (midiInGetDevCaps(d,&caps, sizeof(caps))==MMSYSERR_NOERROR) {
    printf("%c%d: %s\n",d==devid?'*':' ',d,caps.szPname);
    }
    }
    HMIDIIN hmidi={};
    if (midiInOpen(&hmidi, devid, (DWORD_PTR)midicb, 0, CALLBACK_FUNCTION)!=MMSYSERR_NOERROR) return printf("failed to midiInOpen\n");
    if (midiInStart(hmidi)!=MMSYSERR_NOERROR) return printf("failed to midiInStart\n");
    while (_getch()!=27) ;
    midiInStop(hmidi);
    midiInClose(hmidi);
    return 0;
    }

    ////////////////////////////// simple ini parser
    char *settings=0;
    void LoadIniSettings() {
    settings="";
    FILE *f=fopen("midi2osc.ini","rb");
    if (!f) return ;
    fseek(f,0,SEEK_END);
    int l=ftell(f);
    char *dst=settings=(char*)calloc(1,l+4);
    fseek(f,0,SEEK_SET);
    while (!feof(f)) {
    char *odst=dst;
    char linebuf[256]={};
    if (!fgets(linebuf,255,f)) break;
    const char *src=linebuf;
    while (*src && isspace(*src)) ++src; // skip white space
    if (*src=='#') continue; // comment
    const char *ks=dst;
    while (*src && !isspace(*src) && *src!='=') *dst++=*src++; // copy key
    if (dst==ks) {dst=odst; continue; } // empty key
    *dst++=0; // null terminate key
    while (*src && isspace(*src)) ++src; // skip white space after key
    if (*src++!='=') {dst=odst; continue; } // no equals sign
    while (*src && isspace(*src)) ++src; // skip white space after =
    const char *vs=dst;
    while (*src && *src!='\r' && *src!='\n' && *src!='#') *dst++=*src++; // copy value to end of line
    while (dst>vs && isspace(dst[-1])) --dst; // trim trailing spaces
    *dst++=0; // null terminate value
    }
    fclose(f);
    *dst++=0; *dst++=0; // empty string to terminate settings
    }
    const char *GetIniSetting(const char *key, const char *default_value) {
    if (!settings) LoadIniSettings();
    for (const char *s=settings;*s;) {
    const char *val=s+strlen(s)+1;
    if (strcmp(s,key)==0) return val;
    s=val+strlen(val)+1;
    }
    return default_value;
    }