back

MIDI
to control SimHub plugins
and for SimHub to control external tactile signal effects,
e.g. a VST multichannel fader plugin in VoiceMeeter

About MIDI: servers, clients, senders, receivers, control surfaces, sequencers,
                        editors, recorders, instruments
Historically, MIDI (Musical Instrument Digital Interface) was a variation on asynchronous serial communication,
using the same UART technology as in serial printers, terminals and modems,
but with idiosynchratic 31.5kbps data rate, 5-pin (ground + 2 in + 2 out) DIN connectors and 5 Volt signals,
instead of RS-232's D-subminiature 25 or 9-pin connectors with +/-12 Volt signals.

More crucially, while RS-232 is essentially point-to-point between data terminals and carriers,
MIDI can daisy-chain, with signals passed among devices that can both initiate and receive packets.
Consequently, MIDI packets include addressing for 16 channels, to selectively send and receive packets along a shared bus.

While all MIDI devices may send and receive messages, instruments are typically receivers,
while control surfaces and sequencers are typically senders.
Once IP networking is involved, server and client roles get involved.
Either senders or receivers can be either servers or clients.
Since web browsers can play music, JavaScript to behave as client instruments is relatively popular.
At any rate, code for Websocket MIDI control surface servers is relatively rare.

MIDI 1.0 messages mostly include 3 bytes, one for status and 2 for data.
  • Data bytes are constrained to have msb=0, so only 7 bits for information.
  • The status byte has most-significant bit = 1, 4 least significant bits for addressing 16 channels,
    leaving 3 bits for message types, 7 of which (8x - Ex) are for channel messages:
    Status BytehexData Byte1 Data Byte2
    Note off 8xKey number Note Off velocity
    Note on 9xKey number Note on velocity
    Polyphonic Key PressureAxKey numberAmount of pressure
    Control Change (CC) BxController 0-120 Controller value
    Control Mode Bx79: reset all 00
    Control Mode Bx7A: Local 0:off 127:on
    Control Mode Bx7B: All notes off00
    Control Mode Bx7C: Omni off 00
    Control Mode Bx7D: Omni on 00
    Control Mode Bx7E: Mono on 0 or channel cnt
    Control Mode Bx7F: Poly on 00
    Program Change CxProgram number None
    Channel Pressure DxPressure value None
    Pitch Bend ExMSB LSB
    SysEX start F0many bytes ...
    Timing code F1only 1 byte (na)
    Song position F2MSB LSB
    Timing code F3only 1 byte (na)
    Timing code F6(na) (na)
    SysEX stop F7(na) (na)
    Timing clock F8MSB LSB
    Start sequence FAMSB LSB
    Continue sequenceFBMSB LSB
    Stop sequence FCMSB LSB
    Active sensing FEMSB LSB
    System reset FFMSB LSB

    MIDI messages F4, F5, F9, FD are undefined.
    Control Change (CC) messages are of particular interest here;
    many second byte values support 127 third byte values and appropriate for sliders:
    Hex 00-13 for MSB and 20-33 for corresponding LSB.
    First data byte values 70-83, 85-95 are for LSB-only second data bytes
Juan P Bello's MIDI Code pdf

Wireless MIDI
MIDI control of SimHub and VoiceMeeter was first tried by Wi-Fi
e.g. from smartphone or ESP32.  Instead, it is now by MIDIio and KORG nanoKONTROL2.


ESP32-S2 USB MIDI does not composite well with other devices (CDC, HID gamepad)
Instead, send MIDI over Wi-Fi, preferably by WebSocket
Wireless MIDI flavors
  • DSMI - old protocol for Nintendo DS
  • ipMIDI - proprietary $79 (60 min. free trial)
    One of two MIDI-over-UDP protocols, uses "well-known" port;
    Arduino can be a relatively simple client.
  • Apple MIDI - AKA RTP MIDI UDP depending on DNS broadcast
    Arduino must be a relatively complex server
  • WebSocket MIDI - similar to Apple MIDI, except by TCP WebSocket instead of UDP
      For ESP32, AsyncUDP is bundled with Arduino core,
      but separate from AsyncTCP wanted for efficient webserver,
      making this perhaps the most efficient implementation.
     
    Apple MIDI parser bundled in the Arduino library does not seem robust;
      alternatives exist, e.g. in librtpmidid (C++) and MIDIMonster (C) backend
      rtpmidid includes good-looking tests, despite C++...
  • WebMIDI - NOT WebSocket MIDI; MIDI device access from web browsers
    WEBMIDI.js   GitHub examples
Android Wireless Mixer app using DSMI
Trajkovski Labs support page
Installation
Wireless Mixer MIDI map (CC numbers, decimal)     Master volume = 7
    Play = 118
    Stop = 120
    Record = 119
    Left = 4
    Right = 5
    Previous = 2
    Next = 1
Slider1 = 8
Slider2 = 9
Slider3 = 10
Slider4 = 12
Slider5 = 13
Slider6 = 14
Slider7 = 15
Slider8 = 16
Slider9 = 17
Slider10 = 18
Slider11 = 19
Slider12 = 20
    Pan1 = 23
    Pan2 = 24
    Pan3 = 25
    Pan4 = 26
    Pan5 = 27
    Pan6 = 28
    Pan7 = 29
    Pan8 = 30
    Pan9 = 31
    Pan10 = 33
    Pan11 = 34
    Pan12 = 35
    Knob1-1 = 39
    Knob1-2 = 40
    Knob1-3 = 41
    Knob1-4 = 42
    Knob1-5 = 43
    Knob1-6 = 44
    Knob1-7 = 45
    Knob1-8 = 46
    Knob1-9 = 47
    Knob1-10 = 48
    Knob1-11 = 49
    Knob1-12 = 50
    Knob2-1 = 53
    Knob2-2 = 54
    Knob2-3 = 55
    Knob2-4 = 56
    Knob2-5 = 57
    Knob2-6 = 58
    Knob2-7 = 59
    Knob2-8 = 60
    Knob2-9 = 61
    Knob2-10 = 62
    Knob2-11 = 63
    Knob2-12 = 65
    Knob3-1 = 102
    Knob3-2 = 103
    Knob3-3 = 104
    Knob3-4 = 105
    Knob3-5 = 106
    Knob3-6 = 107
    Knob3-7 = 108
    Knob3-8 = 109
    Knob3-9 = 110
    Knob3-10 = 111
    Knob3-11 = 112
    Knob3-12 = 113
Mute1 = 66
Mute2 = 67
Mute3 = 68
Mute4 = 69
Mute5 = 70
Mute6 = 71
Mute7 = 72
Mute8 = 73
Mute9 = 74
Mute10 = 75
Mute11 = 76
Mute12 = 77
    Solo1 = 78
    Solo2 = 79
    Solo3 = 80
    Solo4 = 81
    Solo5 = 82
    Solo6 = 83
    Solo7 = 84
    Solo8 = 85
    Solo9 = 86
    Solo10 = 87
    Solo11 = 88
    Solo12 = 89
    Rec1 = 90
    Rec2 = 91
    Rec3 = 92
    Rec4 = 93
    Rec5 = 94
    Rec6 = 95
    Rec7 = 96
    Rec8 = 97
    Rec9 = 98
    Rec10 = 99
    Rec11 = 100
    Rec12 = 101
    ExBtn1_1 = 11
    ExBtn1_2 = 21
    ExBtn1_3 = 22
    ExBtn1_4 = 32
    ExBtn1_5 = 36
    ExBtn1_6 = 37
    ExBtn1_7 = 38
    ExBtn1_8 = 51
    ExBtn1_9 = 52
    ExBtn1_10 = 124
    ExBtn1_11 = 125
    ExBtn1_12 = 126
    ExBtn2_1 = 114
    ExBtn2_2 = 115
    ExBtn2_3 = 116
    ExBtn2_4 = 117
    ExBtn2_5 = 121
    ExBtn2_6 = 122
    ExBtn2_7 = 123
    ExBtn2_8 = 127
    ExBtn2_9 = 64
    ExBtn2_10 = 6
    ExBtn2_11 = 5
    ExBtn2_12 = 3(?)
 
WebMIDI over WebSocket directly from the web page
A general purpose MIDI over WebSocket approach
  using EventEmitter JavaScript
 
MIDI over WebSocket
ESPAsyncWebServer examples include WebSocket server and client
ESP32 WebSocket Server - Random Nerd
esp32 WebSocket client - Mischianti
ESP32 Arduino Tutorial: Websocket client - DFRobot 2017
ESP32 Arduino: Websocket client
  ESP8266-Websocket server and client examples
  simple-client sketch
HTML5 - WebSockets tutorial with Client-side HTML & JavaScript code
OBS-Websockets-MIDI Bridge
 
PC MIDI servers
MCPW.EXE, seemingly compatible with RTP MIDI or TCP;
  requires a separate MIDI port provider, e.g. loopMIDI
rtpMIDI server
mnet MIDIHub 32/64-bit WinMM MIDI driver, our designated bridge between IP and MIDI,
  supports ipMIDI, RTP, WebSocket, WebMIDI and Bluetooth;
  behaves as a client for UDP, but a server for ipMIDI AKA multicast IP.
It also behaves as a client for TCP WebSocket
looking for servers to announce (via DNS) MIDI service.
Apple MIDI (AKA RTP) uses one UDP port for control and another for MIDI messages
Whether it wants one or two ports for TCP WebSockets is unclear.
  provides its own MIDI ports:


  • as warned, MIDIHub provokes firewall pop-ups for MIDI apps
  • "owned" by VoiceMeeter on initial launch
  • Its documentation suggests that it can be both a websocket server and client.
  • mnet.log reports: WebSocketIO - server listening on port: 6504
  • Web browser JavaScript is not allowed to create a WebSocket server,
    but a JavaScript client to open a WebSocket to that port
    and try sending some MIDI messages may not be too hard;
    here are an article and sample browser code.
    • mnet MIDIHub does not seem to work as described
      supposedly hidden by default, multicast ports appear
    • RTP ports seemingly only appear if detected by multicast DNS
      service_type evidently should be _apple-midi.
  • Writing WebSocket servers
    responding to pings.
Node.js WebSocket MIDI servers
 
Apple MIDI over UDP
RTP MIDI:   Apple network MIDI (UDP),   AppleMIDI with ESPAsyncWebServer examples
Apple's MIDI Network Driver Protocol Document
 
Hairless MIDI<->Serial Bridge
connect serial devices (e.g. Arduinos) to send and receive MIDI signals
LibreConnect Server
generates websockets for serial USB-attached Arduinos

Learning by doing

  • AsyncUDP example - library provided with Expressif ESP32 core for Arduino;
    may be extended for MIDI support as a learning step; AppleMIDI for Arduino supports ESP32
  • AsyncTCP example - GitHub library; will be extended for MIDI support,
    to avoid running both it (for webserver) and AsyncUDP
    • this sketch can only make a single response per opening
  • Restarting Serial Monitor for either of the above examples restarts the connection..
  • ESP32_NoteOnOffEverySec.ino from Arduino-AppleMidi-Library examples
  • wESP32_NoteOnOffEverySec.ino includes MDNS.addService("apple-midi", "udp", AppleMIDI.getPort());
    • replaced ETH.begin(); in ETH_Heelper.h:ETH_startup() with WiFi.begin()
    • MIDIHub recognizes the device, and the sketch reports:
      Connected to session 4155933516 mnet (ALIENWARE-R7_144) 8,
      ...but also reports *** ListenerTimeOutException and many *** ParseException,
      which occur in AppleMIDI.hpp:parseControlPackets(),
      based on UnexpectedData returned from AppleMIDI_Parser.h: parse() returning UnexpectedData
      which it does for too many reasons: signature, protocolVersion,
      or not having previously returned something.
    • Adding debugging code to AppleMIDI_Parser.h revealed that
      most ParseExceptions were an unexpected and long signature.
    • Added more debugging code to understand AppleMIDI_Parser
      then MIDI traffic sometimes began flowing:
      ESP-ROM:esp32s2-rc4-20191025
      Build:Oct 25 2019
      rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
      SPIWP:0xee
      mode:DIO, clock div:1
      load:0x3ffe6100,len:0x570
      load:0x4004c000,len:0xa50
      load:0x40050000,len:0x28d8
      entry 0x4004c18c
      wESP32_NoteOnOffEverySec.ino Booting
      Establishing connection to WiFi..
      Establishing connection to WiFi..
      Establishing connection to WiFi..
      Establishing connection to WiFi..
      Establishing connection to WiFi..
      Connected to network as  192.168.1.185
      OK, now make sure an rtpMIDI session is Enabled
      Add device named Arduino with Host 192.168.1.185 Port 5004
      The device should also be visible in the directory as AppleMIDI-ESP32
      Select and then press the Connect button
      Then open a MIDI listener and monitor incoming notes
      amSignature match
      amSynchronization match
      *** ParticipantNotFoundException -772189380
      amSignature match
      amSynchronization match
      *** ParticipantNotFoundException -772189380
      amSignature match
      amSynchronization match
      *** ParticipantNotFoundException -772189380
      amSignature match
      amInvitation match
      amProtocolVersion match
      amSignature match
      amInvitation match
      amProtocolVersion match
      amSignature match
      amInvitation match
      amProtocolVersion match
      Connected to session 3522777916 mnet (ALIENWARE-R7_144)
      first MIDI.sendNoteOn()
      MIDI-OX must connect to the configured MIDIHub port (e.g. MIDIHUB PORT 2) after
    • Without Serial Monitor running and with MidiView already watching MIDIHUB PORT 2,
      MIDI traffic seems to flow OK..
    • MIDIHub sometimes allows both MIDI_OX and MidiView to simultaneously read MIDIHUB PORT.
  • Arduino ipMIDI Transport on MIDIHUB PORT 1
    The
    ESP32_NoteOnOffipMIDI sketch seemed too simple to work with MIDIHub..
    but it sometimes does.
    It seems to work more consistently when Serial Monitor is used until notes start,
      then connecting MidiView to MIDIHUB PORT 1,
    but it still (also) occasionally stops for no known reason..
    This page describes IP routing magic for ipMIDI.
  • Arduino ESP32 Sketch Data Upload tool installation
    • Download the latest esp32fs.zip file
    • Unzip esp32fs.jar into arduino-1.8.16\tools\ESP32FS\tool\
    • Launch Arduino and check for ESP32 Sketch Data Upload under Tools:
    • Files to be uploaded should be in a Data folder inside the sketch folder:
    • Launch that tool after uploading the sketch,
      then in Serial Monitor:   SPIFFS mounted successfully
      take care to select SPIFF:  
       
  • ESP32-SPIFFS-RGB-sliders uses WebSockets.
    The Arduino server updates all webpage clients to current slider settings;
    need to sort how to separately send binary MIDI to mnet MIDIHub
  • midi-websocket-fa-m client connects to mnet MIDIHUb
    and attempts to send MIDI to it (so far, unsuccessfully)
  • NodeJS MIDI websocket server, for debugging the above MIDI websocket client
    and perhaps capturing traffic from a mnet MIDIHub client
maintained by blekenbleu