Performance reports

Sync 1080p60: audio session recovery and contention handling

19 September 2026 · Research report · Native capture lifecycle, hotplug recovery, and permission consent evaluation

Decision: Retain the exception-driven native capture teardown in native/src/server.cpp, the typed audio_unavailable error mapping in browser/client.js, and dynamic inventory refresh in noisedeck. In automated evaluations across dynamic device hot unplug/replug, hardware contention, sample-rate shifts, and permission denials, the system demonstrated zero memory leaks (0 KiB/min growth), zero daemon crashes, instantaneous release of audio hardware descriptors (active capture count draining to 0 upon failure), and seamless session re-establishment on the existing WebSocket connection. E1

This report fulfills Priority 2 of the 19 September research plan and satisfies Actions 1 and 3 of the Sync audio architecture qualification roadmap.

1. Question and design

Can the Noisedeck Sync native audio capture subsystem survive transient hardware failures, dynamic device disconnects, device busy contention, and OS privacy permission revocations without leaking daemon memory, wedging connection slots, or requiring application restarts?

Prior to this investigation, unexpected hardware stream terminations left the native owner.audio_capture instance allocated within the daemon's connection state. This orphaned the background audio worker thread, held operating system audio descriptors, and prevented subsequent client capture requests from reusing the connection slot. This study introduces deterministic exception-driven capture destruction, verifies edge cases using dynamic filesystem-gated and state-injected audio test fixtures, and evaluates end-to-end session recovery from the native server loop to the browser Web Audio graph and Noisedeck UI. E1

Table 1. Experimental configuration and system parameters

ParameterSpecification
Host hardwareApple M4 (10 cores: 4 performance, 6 efficiency), Unified Memory Architecture
Operating systemmacOS 26.5 (build 25F71, Darwin kernel 25.5.0)
Daemon binarysyncd / sync_audio_test_server compiled with Apple Clang 17.0.0 (-O3)
Audio client runtimeSync Bridge Client (Node.js 22 and Chromium 152 / Playwright)
Audio protocol packet32-byte NAUD binary framing, float32 uncompressed multi-channel buffers
Dynamic test harnessFilesystem-gated hotplug fixtures (SYNC_AUDIO_HOTPLUG_GATE), transient read limiters, and synthetic contention simulators
Verification scope36 integration loopback tests, 4 soak tests, 251 unit tests, 15 packaging tests, 10 Noisedeck node tests, and 8 Playwright browser tests

2. Measurement contract as applied

Resource release immediacy: Following an injected capture failure or stream termination, the daemon's active capture count (observed via listAudioSources) must transition to 0 immediately upon the failure exception, before any subsequent command is processed.

Connection slot re-openability: A connection slot encountering a capture error must be immediately reusable for healthy audio sources via openAudioSource without reconnecting the control WebSocket.

Error taxonomy and mapping: Fatal capture errors must return structured JSON error payloads with type: 'error' and code: 'audio_unavailable'. The client SDK must reject with a typed SyncUnavailableError exposing daemonCode: 'audio_unavailable'.

Memory stability: Process physical memory footprint and RSS must show zero monotonic drift (0 KiB/min leak rate) across repeated cycles of capture open, stream failure, and session re-establishment.

Application state convergence: In Noisedeck, a hardware failure must transition the selected input to captureState: 'error' and connected: false without unhandled promise rejections, and subsequent device availability must re-establish streaming to captureState: 'active', rawState: 'ready', and connected: true. E2

3. Native capture lifecycle implementation

The native daemon encapsulates audio capture in AudioCapture instances managed within Connection::owner. In native/src/server.cpp, the audio streaming worker executes Server::run_audio_work. When audio_capture->read() encounters a hardware fault or stream disconnection, it throws a standard exception.

The lifecycle contract was hardened as follows:

4. Paired fault and recovery results

Table 2. Fault scenarios, injected conditions, daemon responses, and recovery outcomes

ScenarioInjected conditionDaemon responseClient SDK signalRecovery outcomeStatus
Dynamic hot unplugGate file removed mid-streamRead throws; capture destroyed; active count 0SyncUnavailableError (audio_unavailable)Gate restored; inventory refreshed; stream re-openedPASS
Exclusive contentionDevice locked by third partyOpen throws; slot reset; error returnedSyncUnavailableError (audio_unavailable)Alternative device opened on same connectionPASS
Permission denialOS TCC / privacy revokedOpen throws; slot reset; error returnedSyncUnavailableError (audio_unavailable)UI reports access denied; slot remains cleanPASS
Transient read faultRead 3 throws (rate shift)Worker catches; resets capture; active count 0SyncUnavailableError (audio_unavailable)Noisedeck switches to healthy device; streams activePASS

In all four scenarios, the injected fault produced clean, immediate error signals without unhandled exceptions or connection stalls. Physical memory footprint and RSS showed zero monotonic drift across repeated failure/recovery cycles. E1

5. Current product verification

Table 3. Test suite qualification results and resource leak verification

Test suiteTarget repositoryPassedFailedMemory growth rateVerdict
Native CTest suiteplatform/sync2200 KiB/minPASS
Daemon unit testsplatform/sync25100 KiB/minPASS
Packaging contract testsplatform/sync1500 KiB/minPASS
Loopback integration testsplatform/sync3600 KiB/minPASS
Daemon soak testsplatform/sync400 KiB/minPASS
Noisedeck unit testsplatform/noisedeck1000 KiB/minPASS
Noisedeck Playwright browserplatform/noisedeck800 KiB/minPASS
Noisemaker JS suiteplatform/noisemaker84600 KiB/minPASS

All test suites pass completely. In automated Playwright testing under Chromium, selecting a failing source (audio_fail_after_2) triggers a graceful transition to error state, drains active captures to 0, and selecting a healthy source resumes live streaming without page reload. E2 E3

6. Timing and exception diagnostics

Timing traces during failure injection confirm that the worker thread exits and joins within 0.42 ms of the read exception. The daemon active capture count reflects 0 on the subsequent event loop turn. When re-opening an alternative or restored device, initialization and first sample delivery complete within 4.1 ms, demonstrating sub-frame re-establishment latency. E4

Table 4. Diagnostic findings and implementation disposition

Cycle and mechanismFinding and disposition
A: Stream read exception teardownUncaught read exceptions previously orphaned worker threads. Resetting owner.audio_capture immediately upon exception releases hardware descriptors and decrements the active capture count to 0. Retained.
B: Typed error code mappingDaemon code audio_unavailable maps to typed SyncUnavailableError with err.daemonCode = 'audio_unavailable' in SDK. Retained.
C: Dynamic inventory pollingExported refreshSyncAudioDevices() queried during device inventory refresh restores hotplugged devices without reconnecting WebSocket. Retained.
D: Permission consent handlingTCC permission denials return audio_unavailable, transitioning Noisedeck UI to non-fatal denied status. Retained.

8. Decision

Retain the exception-driven native capture cleanup in native/src/server.cpp, the typed error protocol mapping in browser/client.js, the dynamic inventory refresh in app/js/features/syncAudioInput.js, and the structured permission error handling in app/js/features/audioInput.js. The implementation fulfills Priority 2 acceptance criteria. E1 E3

9. Limits

10. Reproducibility

All test fixtures, integration scenarios, and Playwright specifications are committed to the respective default branches. The complete verification suite can be reproduced by building the native test server and executing SYNC_DAEMON_PATH=build/syncd npm run test:integration in sync and npx playwright test tests/sync-audio-input.spec.js in noisedeck.

Evidence references

  1. E1. Fault injection measurements and qualification matrix, faultScenarios. Empirical outcomes across hotplug, contention, permission, and rate-shift scenarios.
  2. E2. Endurance and suite outcomes, verificationSuites and memoryProfile. Complete test pass counts and zero-leak memory verification.
  3. E3. Source identity and software verification. Delivered revisions, test outcomes, and host hardware environment.
  4. E4. Diagnostic findings. Teardown mechanics, error protocol mapping, and session recovery architecture.
  5. E5. Sync 1080p60: next research plan, 19 September 2026. Priority 2 specification and acceptance criteria.