Files
el/peripheral
bigmerge 6f3d692784
El SDK CI - dev / build-and-test (pull_request) Failing after 14m23s
Add peripheral — own-core, consent-gated I/O organ
939-line Swift I/O organ (mic/camera capture, speaker playback via
AVFoundation/CoreAudio), own-core LPC voice synthesis/imitation,
consent-gating, and full-duplex barge-in conversation — closing the
hear -> understand -> speak loop entirely on-device.

.gitignore in this dir already excludes bin/ (build output), out/
(captured media), and .consent.json/.resume.json (local runtime state),
so only src + README + .gitignore are committed here.
2026-08-15 14:28:14 -05:00
..

peripheral — Neuron's I/O organ (own-core, local, consent-gated)

The interface made physical. Two afferent senses in, one efferent voice out — all reached the way the agentic surface reaches any tool.

MIC    (hear)  afferent   device -> capture -> descriptor -> ingest -> geometry
CAMERA (see)   afferent   device -> capture -> descriptor -> ingest -> scene-geometry
SPEAKER(speak) efferent   render WAV -> PLAY ALOUD out the speaker

Closes the conversational loop: hear (mic) -> understand (engram) -> speak (speaker).

Rails

  • Own-core. macOS-native only: AVFoundation (camera/mic), CoreAudio voice- processing (AEC), afplay (speaker), ImageIO/CoreGraphics (frames), hand-rolled DSP (WAV, LPC, formant synthesis). No cloud, no heavy deps.
  • Local-only. Raw streams are written to out/ and never egress. .gitignore keeps captured media out of git.
  • Consent-gated (two locks). A Neuron-level grant (grant/revoke) and the OS TCC permission. Sensitive senses (camera/mic) fail closed without both.
  • Disclosed. Every device touch prints a [peripheral] line on stderr.

Build

swiftc -O -o bin/periph src/periph.swift \
  -framework AVFoundation -framework CoreMedia -framework Foundation \
  -framework CoreGraphics -framework ImageIO -framework CoreImage

Commands

periph grant|revoke <camera|mic>          # Neuron-level consent
periph status
periph speak <file.wav>                   # SPEAK ALOUD (efferent)
periph tone <out.wav> [hz] [sec]          # own-core WAV synth
periph listen <sec> <out.wav>             # MIC capture (afferent), 16k mono
periph see <out.jpg>                      # CAMERA one frame (afferent)
periph feat-audio <wav> | feat-image <jpg>          # capture -> compact descriptor
periph ingest-audio|ingest-image <file> <engramURL> # descriptor -> engram node (geometry)
periph voiceprint <voice.wav>             # extract F0 + formants F1-F5
periph imitate <voice.wav> <out.wav>      # speak back in that voice (LPC resynthesis)
periph hear-imitate <sec> <out.wav>       # MIC -> signature -> imitate -> SPEAK ALOUD
periph converse <manifest.json> [--authority F] [--barge-at S[:backchannel|:bargein]] [--resume] [--live-mic]

The afferent metabolism

A capture is never shipped raw. It becomes a compact descriptor — the afferent twin of the music instrument-signature:

  • audio -> [seconds, sr, ch, rms, peak, zcr, centroid, F0] (~2400-6000x smaller)
  • image -> [w, h, meanRGB, brightness, 3x3 luminance grid] (~400000x smaller)
  • voice -> [F0, F1..F5, bandwidths] (11 numbers)

That descriptor is what the ingest organ (engram POST /api/nodes) turns into an embedded node = geometry.

Voice by imitation

voiceprint/imitate are own-core LPC (autocorrelation + Levinson-Durbin, order 16 @ 16 kHz), formant extraction from the LPC spectral envelope, and source-filter resynthesis (glottal impulse train at F0 through the all-pole formant filter). A voice is grabbed by ear as ~a dozen numbers and spoken back — no training, no stolen voice. Measured fidelity on real speech: resynthesized formants match the source within 2-3%. The full phoneme->formant path for novel sentences is the speech faculty's seam (elp audio surface profile); this engine provides the formant synthesis primitive it renders through.

Interruptibility (native turn-taking)

converse plays the utterance as an ordered, salience-tagged meaning-plan while the mic listens (full-duplex, AEC on so it never barges in on its own voice):

  • barge-in: user speech -> pause on the spot (sample-accurate), not "finish the buffer."
  • yield-or-hold: a decision grounded in the current segment's salience + progress
    • the interrupter's authority — YIELD (stop) or HOLD ("hang on, let me finish").
  • backchannel ("mm-hm"): brief/low -> keep going, resume seamlessly.
  • resumable: on yield the remaining plan persists (.resume.json); --resume picks the thread back up ("as I was saying").

Live full-duplex uses --live-mic (OS AEC). Injected --barge-at drives the decision loop deterministically for testing.