Skip to content

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 19:08
· 68 commits to main since this release
2f877b9

What's Changed

The big change is support for the Sendspin 1.0.0-rc1 protocol via #140. Every connection is now encrypted with Noise (KKpsk2), and a server must pair with the device before it can start playback, unless the application turns on unpaired access with set_unpaired_access_enabled() (off by default). The client's identity is a persisted X25519 keypair, so client_id is now the base64url public key rather than a configured string or MAC address.

Important

rc1 is not wire-compatible with earlier protocol versions. Music Assistant needs 2.11.0b4 or newer, and older servers can no longer connect to a client built on this release.

The other major changes:

  • Protocol task (#148 - #154): a library-owned task now does all protocol work: it owns every connection and receives through one shared inbound ring. Transports only receive, loop() only drains the inbox, and connect_to() / disconnect() can be called from any thread. Task stacks are derived from a static call graph instead of picked by hand (tools/stack_usage/).
  • Source role (#164 - #168, #172, #175): streams captured audio to the server (source@v1) as PCM or Opus. examples/source_client/ streams the default PortAudio input.
  • Opus is a build option (#126): SENDSPIN_ENABLE_OPUS (CMake option / Kconfig entry, on by default).

Summary of breaking changes

Protocol and server compatibility (#140)

  • Requires a Sendspin 1.0.0-rc1 server (Music Assistant 2.11.0b4 or newer).
  • SendspinClientConfig::client_id is removed. The ID comes from the persisted keypair; read it with SendspinClient::client_id() after start().
  • SendspinGoodbyeReason gains UNAUTHORIZED, PAIRING_REQUIRED, CONCURRENT_ATTEMPT and UNPAIRED.

Pairing (#140)

  • New SendspinClientConfig fields choose how a device can be paired: pairing_code_out_channels / pairing_code_formats (dynamic code), static_pairing_code, pairing_window_supported, pairing_psk (the factory Pairing PSK; generated and persisted when unset), the *_locations hints, and max_pairing_records (default 12).
  • New optional SendspinClientListener callbacks: on_pairing_started/succeeded/failed, on_trust_changed, on_display_pairing_code / on_clear_pairing_code, and on_open_pairing_window / on_close_pairing_window.
  • New client methods: confirm_pairing_window(), cancel_pairing_window(), pairing_token() / format_pairing_token(), and set_unpaired_access_enabled().
  • send_text() and get_current_trust() are removed (#152). Track trust through on_trust_changed().

Persistence provider (#140, #145)

  • The typed methods (save/load_last_server_hash, save/load_static_delay) are replaced by an opaque byte store: load_blob(key), save_blob(key, data, len), and commit(). The keys and fixed blob sizes are in persistence_keys.h, and persistence_codec.h exposes the encoding. A provider stores bytes and never parses them.
  • erase_blob() is removed. A revoked record's slot is overwritten with a zeroed blob instead.
  • commit() runs after any batch of writes that holds pairing material. A provider that writes through in save_blob() can keep the default.
  • Values saved by v0.8.0 are not migrated.

Client API

  • start_server() is removed (#147); use start().
  • update_state() and SendspinClientState are removed (#140). Report availability with set_available(), and leave the group with leave().
  • connect_to() and disconnect() can now be called from any thread.
  • websocket_payload_location is replaced by inbound_ring_location (#148).
  • New ESP-IDF task settings: protocol_task_psram_stack, protocol_task_priority, protocol_task_stack_size, httpd_stack_size and websocket_stack_size. Their defaults come from the derived stack budgets (#150), and smaller values are clamped up to them.

Player

  • "Static delay" is renamed "output delay" to match the spec: initial_output_delay_ms, update_output_delay(), set_output_delay_adjustable(), get_output_delay_ms(), on_output_delay_changed().
  • get_fixed_delay_us() is removed (#158).
  • PlayerRoleConfig audio formats must include FLAC or PCM; an Opus-only list is rejected (#126).

Other roles (#140)

  • Artwork: BMP is removed from the image formats (rc1 supports only JPEG and PNG).
  • Visualizer: request_format() and VisualizerFormatRequest are removed.
  • Controller: send_command() now returns bool.

All changes

  • Cache host CI compiles with ccache (#183) @kahrendt
  • Compile host builds through ccache when it is installed (#181) @kahrendt
  • Run clang-tidy on every core (#182) @kahrendt
  • Stamp every inbound message when its last byte arrives (#179) @kahrendt
  • Judge visualizer staleness from the delivery time, not the receive stamp (#180) @kahrendt
  • Dismiss the pairing prompt when a server/activate cancels the attempt (#177) @kahrendt
  • Move task stack derivation from config.h to tools/stack_usage (#176) @kahrendt
  • Ride out source send stalls in the capture buffer (#175) @kahrendt
  • Wait on what three lifecycle tests assert instead of a racing signal (#173) @kahrendt, @claude
  • Pin CPace against the official draft-irtf-cfrg-cpace-21 test vectors (#170) @kahrendt, @claude
  • Fix source capture review findings (#172) @kahrendt
  • Add a source role example (#168) @kahrendt, @chrisuthe
  • Add an Opus encode path to the source role (#167) @kahrendt, @chrisuthe
  • Add the source role with a PCM streaming pipeline (#166) @kahrendt, @chrisuthe
  • Add the outbound ring and the lent encrypted send path (#165) @kahrendt, @chrisuthe
  • Add the source@v1 protocol layer (#164) @kahrendt, @chrisuthe
  • Drop the connection on any send that fails after the Noise encrypt (#163) @kahrendt
  • Drop a disconnected connection through drop_connection() (#162) @kahrendt
  • Call the connection's one owner directly instead of through callbacks (#161) @kahrendt
  • Gate player audio on the adopted client/state snapshot (#160) @kahrendt
  • Keep one admission flag and one time-sync gate on the burst path (#159) @kahrendt
  • Drop history wording from three playback-progress slot comments (#157) @kahrendt
  • Share one hand-over and wake mechanism across the binary roles (#156) @kahrendt
  • Return the sync task to idle on a decode allocation failure, close the review gaps (#154) @kahrendt
  • Hand artwork parts to the decode thread as ring items (#153) @kahrendt
  • Build and parse every protocol document in the JSON arena, one document at a time (#151) @kahrendt
  • Derive the task stacks and ring floor, number stream starts, reap released connections, trim (#150) @kahrendt
  • Run the connection lifecycle on the protocol task and finish the main-loop boundary (#149) @kahrendt
  • Resolve the time filter through a dedicated slot (#146) @kahrendt
  • Stamp client/time at the socket write and keep server/time off the event ring (#143) @kahrendt
  • Store the liveness receive stamp as a 32-bit atomic (#144) @kahrendt
  • Trim internals.md to cross-file facts and split out playback-sync.md (#142) @kahrendt
  • Bind the WS server with one loop() tick instead of a 50 ms window in tests (#141) @kahrendt
  • Fix stale test docs and treat library warnings as errors in CI (#137) @marcelveldt
  • Add an ESP-IDF build check to CI (#138) @marcelveldt
  • Store ConnectionManager's bounded connection lists inline (#136) @kahrendt
  • Tweak the test standards skill and project convention (#135) @kahrendt

Breaking Changes

  • Remove PlayerRole::get_fixed_delay_us() (#158) @kahrendt
  • Post lifecycle requests as slots, close an admitted drop, remove the dead API and the redundant guards (#152) @kahrendt
  • Run the receive path on a library-owned protocol task through one shared inbound ring (#148) @kahrendt
  • Remove the deprecated start_server() alias (#147) @kahrendt
  • Collapse the persistence deferral channels and add a provider commit hook (#145) @kahrendt
  • Implement Sendspin 1.0.0-rc1 protocol (#140) @kahrendt, @raine-works
  • Make the Opus decoder a build option and require flac or pcm in player formats (#126) @marcelveldt, @kahrendt

Dependencies

2 changes

Full Changelog: v0.8.0...v0.9.0