Skip to content

feat(dart): add idiomatic voice runtime interfaces - #50

Merged
harshsaver merged 1 commit into
october-dev:mainfrom
Pushkraj-Space:issue-34-idiomatic-runtime-interfaces
Sep 25, 2026
Merged

harshsaver merged 1 commit into
october-dev:mainfrom
Pushkraj-Space:issue-34-idiomatic-runtime-interfaces

Conversation

@Pushkraj-Space

Copy link
Copy Markdown
Contributor

Summary

Issue #34 needs one idiomatic Dart runtime surface that wearables, phone microphones, headsets, fixtures, and future connectors can implement without coupling host code to Flutter, hardware, providers, storage, or generated protobuf classes.

This change:

  • replaces the Dart SDK's opaque SessionControl body and audio-format map with typed murmur.v1 models
  • preserves the presence of known default-valued fields, including open: false, frameDurationMs: 0, and empty command objects
  • adds pure-Dart VoiceConnector and VoiceSession interfaces plus SessionState and typed VoiceError
  • defines terminal session, cancellation, error ordering, stream buffering, and shared cleanup behavior in the public API documentation
  • adds a deterministic test-only connector/session fake covering discovery, idle connection, capture, cancellation races, late acquisition cleanup, terminal device errors, paused and unlistened streams, and bounded frame dropping
  • updates the Dart SDK documentation and clarifies the checked-in fixture presence rule in the conformance documentation

Wire compatibility

AudioEncoding, AudioFormat, and CaptureMode map directly to the existing murmur.v1 schema. StartSession, StopSession, SetInputGate, and FinalizeSession model the four existing control oneof variants, with nullable fields representing absence.

AudioFrame.payloadBase64 remains verbatim, while its format is now validated and typed. Session-scoped sequence and producing-host monotonic timestamp semantics are documented on the model.

This PR does not change protobuf definitions, shared fixtures, events.dart, source.dart, or other language SDKs.

Runtime lifecycle

  • connect returns an idle session and capture begins only through start
  • stopped and error sessions are terminal, with getters remaining readable
  • concurrent and repeated stop/close calls share one cleanup future
  • a winning stop/close cancels a pending start with VoiceError(code: "cancelled")
  • late native acquisition is released without state or frame resurrection
  • device failures publish the error cause before the error-state transition
  • cleanup closes streams without awaiting consumer-dependent drainage
  • the test fake buffers at most two undelivered frames and drops the oldest

Validation

  • make check-dart — format, analyzer, shared conformance tests, and 19 total Dart tests pass
  • make check-flutter — analyzer and both Omi application tests pass
  • git diff --check

Closes #34

@harshsaver

Copy link
Copy Markdown
Member

Thanks @Pushkraj-Space. The typed models map 1:1 to session.proto, the old untyped API is gone with no shim, and presence-preserving nullables are justified by the "open": false fixture. make check-dart passes locally (19 tests).

Optional nits (fine as follow-ups):

  • lib/src/runtime.dart: close() is documented as identical to stop(). Unless a host needs both names, one method is simpler.
  • lib/src/runtime.dart: the in-process SessionState shares its name with the wire enum but has no warmMuted. A one-line doc note saying warm-muted belongs to the coordinator (per docs/voice-runtime.md) would prevent confusion.
  • conformance/README.md:36-37: "Accepted fixtures are canonical: default-valued fields are omitted" now sits right next to a rule about preserving explicit defaults, and session-control.jsonl:3 has one. Please reconcile the two sentences.
  • test/runtime_test.dart:471-773: feat(dart): add provider-neutral voice capture coordinator #51 adds a second fake in test/support/. Consider moving this one there so both share it.

Merging; please rebase #51 afterwards.

@harshsaver
harshsaver merged commit f5510eb into october-dev:main Sep 25, 2026
5 checks passed
nikhilcrypto0 added a commit to nikhilcrypto0/murmur that referenced this pull request Sep 25, 2026
Rewrite the lifecycle around VoiceConnector/VoiceSession from october-dev#50: connect
returns an idle session, stop and close share one terminal cleanup, and
muting and finalization belong to the capture coordinator. Use snake_case
error codes, describe backpressure as the bounded frames contract, and
replace the TypeScript example with a tested Dart SyntheticToneConnector.
harshsaver pushed a commit that referenced this pull request Sep 30, 2026
* docs(connectors): complete the voice connector authoring guide

Adds docs/connector-authoring.md, covering connector ownership, the
manifest, lifecycle and capability behavior, discovery and permissions,
audio format and framing, timestamps, backpressure, interruption and
cleanup, errors, automated/conformance/physical validation, and a protocol
provenance and license checklist.

connectors/examples/synthetic-tone is a minimal example connector at the
murmur.v1 message level. It takes SessionControl and emits VoiceSource,
AudioFrame and RuntimeEvent via the TypeScript SDK models, with pull-based
frames, sample-derived timestamps, idempotent stop/disconnect and stable
error codes. Its tests run under make check-typescript and round-trip every
emitted message through the SDK parser.

check_conformance.py now validates every connectors/**/connector.json, not
only Omi's. A connector proposal issue form and links from CONTRIBUTING,
the README and connectors/README.md complete the checklist.

Closes #35

* docs: align connector guide with the Dart runtime interfaces

Rewrite the lifecycle around VoiceConnector/VoiceSession from #50: connect
returns an idle session, stop and close share one terminal cleanup, and
muting and finalization belong to the capture coordinator. Use snake_case
error codes, describe backpressure as the bounded frames contract, and
replace the TypeScript example with a tested Dart SyntheticToneConnector.

* fix: deliver example session states asynchronously

A stateChanges listener that called stop() ran inside the synchronous
transition, so the nested add threw before cleanup started and the frame
clock leaked. Deliver state changes asynchronously, as the VoiceSession
contract allows, and test stop() from a listener on starting and listening.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Core] Finish runtime interfaces on murmur.v1

2 participants