organ: el gets a speaker, and fetches the voice from the engram
El could turn meaning into samples and could not make a sound. Every path from those samples to the air ran outside the language, through a 939-line Swift program that shelled out to afplay, so the voice was not a capability of El or of Neuron but a separate binary standing next to them. Two things land here. The speaker. el_audio_darwin.m is a CoreAudio AudioQueue realizer in its own translation unit, declared in el_runtime.h, deliberately not a patch to el_runtime.c — acquiring a device must not mean editing the middle of the language, the same rule the realizer registry follows for modalities. It takes samples straight out of memory, so nothing is written to disk and no process is spawned between the intent to speak and the sound. The async half (play/stop/playing/played_frames) exists because barge-in means stopping on the spot, and a blocking play cannot be interrupted. el_peripheral_null.c is the same entry points everywhere else, so El that speaks links anywhere and truthfully reports having no speaker. The voice. organ_voice_fetch asks the engram for a voice region by query and reads the geometry off the node that comes back. A voice is not a JSON file next to the code; it is a memory, and the organ retrieves it the way anything retrieves a memory. An absent region returns empty rather than a plausible default, because a caller must be able to tell 'this is how they sound' from 'I never heard them'. Underneath both: __str_set_char bounds-checked writes against strlen(), which is 0 for the zero-filled buffer __str_alloc hands back, so every write was rejected and every El-authored WAV in this repo was 55,244 bytes of silence that reported ok=true. Byte buffers now carry their capacity in a side table; text keeps the exact strlen behaviour it had. This is why nobody noticed El was mute. Measured: voice fetched from the engram reads f0=137 f0_end=116 kf=1269 f1=500 f2=2093 f3=3531, matching the 30s LPC measurement; render is 20160 samples at 16 kHz; both the rendered utterance and an own-core tone played aloud through CoreAudio with no Swift and no afplay in the chain.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/* el_peripheral_null.c — the no-device build of El's I/O organ.
|
||||
*
|
||||
* Every entry point declared in el_runtime.h's "Peripheral" block, implemented
|
||||
* as an honest refusal. This is what a platform without an El audio/capture
|
||||
* realizer links instead of el_audio_darwin.m + el_capture_darwin.m, so an El
|
||||
* program that speaks or listens still COMPILES AND LINKS everywhere.
|
||||
*
|
||||
* The distinction that matters: these do not pretend. speaker_available() and
|
||||
* mic_available() return 0, and every operation returns its failure sentinel.
|
||||
* A program asking "can I speak here?" gets a truthful no, rather than a
|
||||
* silence it would have to infer something from. Silent success is the failure
|
||||
* mode this whole change exists to eliminate — El spent this entire codebase's
|
||||
* history writing WAV files full of zeros and reporting ok=true, and nobody
|
||||
* caught it because nothing ever said "there is no sound here".
|
||||
*
|
||||
* Compiled INSTEAD OF the Darwin realizers, never alongside them — the symbols
|
||||
* are the same by design, which is the point: the El side never branches on
|
||||
* platform, it branches on speaker_available().
|
||||
*/
|
||||
|
||||
#include "el_runtime.h"
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
|
||||
/* ── Speaker ─────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t speaker_available(void) { return (el_val_t)0; }
|
||||
el_val_t speaker_name(void) { return EL_STR("none"); }
|
||||
|
||||
el_val_t speaker_play_pcm16(el_val_t samples, el_val_t sample_rate) {
|
||||
(void)samples; (void)sample_rate;
|
||||
return (el_val_t)0;
|
||||
}
|
||||
|
||||
el_val_t speaker_play_wav(el_val_t path) {
|
||||
(void)path;
|
||||
return (el_val_t)0;
|
||||
}
|
||||
|
||||
el_val_t speaker_play_pcm16_async(el_val_t samples, el_val_t sample_rate) {
|
||||
(void)samples; (void)sample_rate;
|
||||
return (el_val_t)0;
|
||||
}
|
||||
|
||||
el_val_t speaker_playing(void) { return (el_val_t)0; }
|
||||
el_val_t speaker_stop(void) { return (el_val_t)0; }
|
||||
el_val_t speaker_played_frames(void) { return (el_val_t)0; }
|
||||
|
||||
/* ── Microphone ──────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t mic_available(void) { return (el_val_t)0; }
|
||||
el_val_t mic_request_access(void) { return (el_val_t)0; }
|
||||
|
||||
/* Empty list, not 0: the contract says capture returns samples, and a caller
|
||||
* iterating the result must find nothing rather than dereference a non-list. */
|
||||
el_val_t mic_capture_pcm16(el_val_t seconds, el_val_t sample_rate) {
|
||||
(void)seconds; (void)sample_rate;
|
||||
return el_list_empty();
|
||||
}
|
||||
|
||||
el_val_t mic_monitor_start(void) { return (el_val_t)0; }
|
||||
el_val_t mic_monitor_rms(void) { return el_from_float(0.0); }
|
||||
el_val_t mic_monitor_stop(void) { return (el_val_t)0; }
|
||||
|
||||
/* ── Camera ──────────────────────────────────────────────────────────────── */
|
||||
|
||||
el_val_t camera_available(void) { return (el_val_t)0; }
|
||||
el_val_t camera_request_access(void) { return (el_val_t)0; }
|
||||
el_val_t camera_capture_rgb(void) { return (el_val_t)0; }
|
||||
|
||||
el_val_t camera_capture_jpeg(el_val_t path) {
|
||||
(void)path;
|
||||
return (el_val_t)0;
|
||||
}
|
||||
|
||||
#endif /* !__APPLE__ */
|
||||
Reference in New Issue
Block a user