organ: el speaks — the peripheral becomes a capability of the language #159
Reference in New Issue
Block a user
Delete Branch "feat/el-speaks"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
el speaks
The engram stores geometry and does not speak. The speaking belongs to the language and its runtime.
Before this, Neuron's mouth and ears were a 939-line Swift program (
peripheral/src/periph.swift) that shelled out toafplay. The voice was not a capability of El or of Neuron — it was a separate binary standing next to them, and "speak" meant "ask that binary to speak."ls lang/runtime/*audio*returned nothing and there were zero.elfiles inperipheral/.Now El has a speaker, and it fetches the voice from the engram.
The line this draws
Not between languages — between device and arithmetic.
Stayed a realizer (2 files, and only these)
lang/runtime/el_audio_darwin.mlang/runtime/el_capture_darwin.mBoth are their own translation units, declared in
el_runtime.h, and deliberately not patches toel_runtime.c— acquiring a device must not mean editing the middle of the language, the same rule the realizer registry follows for modalities.el_peripheral_null.cprovides identical entry points elsewhere, so El that speaks links on any platform and truthfully reports having no speaker instead of going quietly silent.speaker_play_pcm16takes samples straight out of memory: nothing is written to disk, and no process is spawned between the intent to speak and the sound.Moved into El (everything else)
organ_dsp.elorgan_dsp.elorgan_dsp.elorgan_dsp.el[seconds, sr, ch, rms, peak, zcr, centroid, F0]organ_dsp.el[F0, F1..F5, bandwidths]organ_dsp.el[w, h, meanRGB, brightness, 3×3 luma grid]organ.elorgan.elorgan_converse.elorgan_cli.elThe voice comes from the engram
A voice is geometry in the engram, not a JSON file next to the code. The previous path parsed
elp/data/will-voice.engram.json, which quietly made the voice a build artifact instead of a memory.organ_voice_fetchissues an engram query and reads the geometry off the node that comes back:Matching the 30 s LPC measurement exactly. An absent region returns empty, never a plausible default — a caller must be able to tell "this is how they sound" from "I never heard them."
The reverse direction (
ingest-voice) closes the loop: a voiceprint becomes a node, and the voice is thereafter a memory rather than a measurement someone wrote down. Verified in one session: a voice learned by ear → ingested → fetched back by query → spoken.The organ never learns a word
Pronunciation, vocabulary and phonemes belong to the language faculty and are already built as ingested geometry — the engram knows how to pronounce. The seam is
synth_codes(codes, voice, pmap): codes and phoneme map arrive from the language side as geometry; the organ turns them into samples and gets the samples out the speaker. No lexicon, no grapheme-to-phoneme, nothing underelp/src/*.eltouched.Measured against the Swift original
Same input,
peripheral/out/mic_room.wav(16 kHz mono, 48121 samples):periphAgreement to every printed digit.
imitatecannot match bit-for-bit because the Swift excites unvoiced frames withDouble.random. Two Swift runs correlate 0.957 with each other; El correlates 0.958 with Swift, diverging at the same sample (39, the first noise-excited one) with a bit-identical deterministic prefix. The port is as close to the original as the original is to itself.The bug underneath all of it
__str_set_charbounds-checked writes againststrlen(p). For the zero-filled buffer__str_allochands back,strlenis 0, soidx >= lenrejected every write. The function was a total no-op on the only kind of buffer it was ever used with.Every El-authored WAV in this repo was 55,244 bytes of pure zeros — correct length, correct filename, no RIFF header,
ok=true. El has been mute for its entire history and nothing said so, because nothing ever reports "there is no sound here."Byte buffers now carry their capacity in a side table keyed by pointer; anything unregistered keeps the exact
strlenbehaviour it had, so text semantics are unchanged. (A length header behind the pointer was rejected:__str_set_characcepts any String including a.rodataliteral, where reading the preceding bytes is undefined.)Also adds
eprintln— El could write to stdout and nowhere else, and a program that announces "I am opening the microphone" on stdout has corrupted its own output.Verified, by running it
strings organ | grep -ic 'afplay|swift'→ 0.pgrep -P <pid>during playback → none. Only OS frameworks linked.f0=137 f0_end=116 kf=1269 f1=500 f2=2093 f3=3531, persisted across processes in a durable store, fetched by query.hear-imitate— 48000 frames heard → LPC signature → 47999 resynthesized → spoken aloud.Build is
./peripheral/build.shfrom the repo root. Local build is the bar; the ObjC realizers compile clean under ARC.What still does not work
--live-micbarge-in in a real room is unproven. The AEC path is implemented and the deterministic path is fully exercised, but acoustic behaviour with a real interrupter is not something a headless run can establish.prov=COARSEsays so on the node.Voice/VowelTargetnodes. They live in the organ's own store and in snapshot files from earlier work. Getting them into prod is an ingest, not a code change.imitateis not reproducible run-to-run, and neither is the Swift, for the same reason.src/periph.swiftis kept as the reference the port was measured against, not as a dependency. Nothing in the chain calls it.Production untouched throughout: own store dir, own ports, read-only GETs only.