Skip to content

4.0.0

Latest

Choose a tag to compare

@adrianfreed adrianfreed released this 04 Aug 18:14
· 135 commits to master since this release

First release since 3.5.8.

⚠️ Breaking: the wire format changed for two types

'r' (RGBA) and 'm' (MIDI) did not interoperate with other OSC software and now do. Both ends of a link must be updated together.

Up to 3.5.8 these two types were encoded and decoded byte-reversed, and 'm' dropped its data2 byte (velocity) while transmitting a channel byte that OSC's 'm' type has no slot for. Because encoder and decoder were reversed symmetrically, CNMAT-to-CNMAT links round-tripped correctly and the fault was invisible — unless you talked to Max, Pd, Reaper or anything else, with which these types could never work.

value 3.5.8 bytes 4.0.0 bytes
oscrgba_t {0x11,0x22,0x33,0x44} 44 33 22 11 11 22 33 44
oscmidi_t {1,0x90,2,0x3C,0x40} 3c 02 90 01 01 92 3c 40

oscmidi_t keeps its five fields, so existing sketches compile unchanged; channel is folded into the low nibble of the status byte on encode.

Bundle timetags. A bundle built without an explicit timetag now sends 0x0000000000000001 — OSC 1.0's "immediately" — rather than 0, which means 1900-01-01. Pass zerotime for the old behaviour. Received timetags are also no longer byte-swapped: setTimetag() read its eight bytes with a raw memcpy, so on a little-endian host a bundle that was decoded and forwarded came back out reversed.

Fixes

  • Integer dispatch — 64-bit values could go out as 32-bit 'i'. Now dispatched across all ten fundamental integer types, which is exact on every platform and needs no <type_traits>, which the AVR core does not ship.
  • SLIPEncodedTCP was a hand-copied duplicate of the serial SLIP state machine and had missed four fixes applied to the original. It is now _SLIPSerial<Client>, so the two transports cannot drift apart again.
  • read() returning -1 was narrowed into fill() as an ordinary 0xFF byte of message content — in the library and in more than twenty examples.
  • Decoders hardened against malformed input; attacker-controlled length fields are bounded by OSC_MAX_INCOMING.
  • OSCMessage gained deep copy and assignment. The implicit versions copied pointers, so a copy and the original freed the same memory.
  • The incoming buffer no longer reallocates once per argument.
  • A permanent ATmega32U4 receive stall is worked around in the library. A USB host ends any transfer that is an exact multiple of 64 bytes with a zero-length packet, and stock USBCore never releases the endpoint bank it lands in — reception stops for good while transmit keeps working.

New

  • OSCBufferedPrint collects a packet and hands it to the transport in a single write, which matters wherever a write is a round trip.
  • Block SLIP transmit, tunable with OSC_SLIP_TX_BUFFER.
  • Boards: Arduino UNO Q, Adafruit nRF52, and the USB Stack: TinyUSB option on Adafruit SAMD and rp2040.
  • WiFi examples covering the current Arduino WiFi libraries, Web Serial pages for seven boards, a host test suite that needs no board, and hardware-in-the-loop probes.

How this was verified

The host suite runs under AddressSanitizer and UBSan and includes an OSC 1.0 decoder written from the spec that shares no code with the library, so agreement with it is evidence rather than self-consistency. Examples were compiled across eight cores. Byte-exact echo, integer-width and SLIP burst tests were run on real hardware: LilyPad USB, Teensy 4.0, RP2040, ESP32-C3, ESP32-S3, UNO R4 WiFi and Esplora.

Thanks to @twhiston (#162), whose report identified the TinyUSB stack problem fixed here, and @peternewman (#86), whose CI proposal is taken up with GitHub Actions.