Compare commits

..

1 Commits

Author SHA1 Message Date
bigmerge b7e2c580a8 Add native speech synthesis and voice-imitation faculty
El SDK CI - dev / build-and-test (pull_request) Waiting to run
speech.el: formant/glottal integer DSP synthesis + voice-analyze-by-
imitation. voice-profile.el / voice-ingest.el: voice-profile plumbing.
accent.el: British-RP as an ingested transform-geometry (explicitly marked
provisional/citation-pending by its own comments). organ-read.el:
engram read-through for the speech organ. Includes demo/test drivers and
non-personal reference data (British-RP phonetics/lexicon derived data,
a public-domain LibriVox RP reference recording).

Deliberately excludes elp/data/live/ (raw recorded voice + face-photo
samples of the repo owner) and the will-*.{json,psv} derived voiceprint
files — personal biometric data that shouldn't be committed to a shared
repo without an explicit decision from the owner. Also excludes this
worktree's elp/src/surface-profile.el, which diverges from the copy in
other worktrees (agent-aaf04b0a9714c4070, main) — needs manual
reconciliation before landing, left out here to avoid silently picking a
version.
2026-08-15 14:27:52 -05:00
22 changed files with 2205 additions and 625 deletions
File diff suppressed because one or more lines are too long
+23
View File
@@ -0,0 +1,23 @@
{
"dataset": "british-rp-accent-transform",
"primitive_type": "accent_target",
"accent": "british-rp",
"grounding": "derived",
"provenance": "HONEST-DERIVED, COARSE FIRST PASS — NOT transcribed measured RP formants. The exact measured RP/GB tables (Deterding 1997 JIPA 27:47-55; Hawkins & Midgley 2005 JIPA 35:183-199) are the intended ground truth but were gated/figure-only at author time and were NOT transcribed. So these targets are DERIVED: each = the corresponding MEASURED Peterson&Barney(1952) base vowel transformed under the documented, citable RP-vs-GA structural rules of Wells (1982) 'Accents of English' — non-rhoticity (NURSE de-rhoticized: remove low F3), TRAP F2-lowering, LOT/THOUGHT back-rounding (F2 down), GOOSE-fronting (F2 up), GOAT centering. Shift MAGNITUDES are coarse/approximate (first pass), directions are cited. ground:derived (base measured + rule cited). Refine by transcribing Deterding/Hawkins&Midgley. No number is presented as a measured RP value it is not.",
"notes": "records with kind=vowel_override REPLACE the base phoneme's formant targets with the DERIVED RP realization. records with kind=rule encode non-formant transforms (non-rhoticity: drop post-vocalic coda /r/). The render composes: base geometry then accent override + rhoticity rule — voice + accent, separable.",
"records": [
{"key": "IY", "features": {"kind": "vowel_override", "set": "FLEECE"}, "attributes": {"f1": 280, "f2": 2249, "f3": 3000}},
{"key": "IH", "features": {"kind": "vowel_override", "set": "KIT"}, "attributes": {"f1": 360, "f2": 2100, "f3": 2550}},
{"key": "EH", "features": {"kind": "vowel_override", "set": "DRESS"}, "attributes": {"f1": 560, "f2": 1970, "f3": 2480}},
{"key": "AE", "features": {"kind": "vowel_override", "set": "TRAP"}, "attributes": {"f1": 730, "f2": 1590, "f3": 2410}},
{"key": "AA", "features": {"kind": "vowel_override", "set": "LOT"}, "attributes": {"f1": 560, "f2": 920, "f3": 2440}},
{"key": "AO", "features": {"kind": "vowel_override", "set": "THOUGHT"}, "attributes": {"f1": 415, "f2": 700, "f3": 2410}},
{"key": "UH", "features": {"kind": "vowel_override", "set": "FOOT"}, "attributes": {"f1": 380, "f2": 1100, "f3": 2240}},
{"key": "UW", "features": {"kind": "vowel_override", "set": "GOOSE"}, "attributes": {"f1": 310, "f2": 1650, "f3": 2240}},
{"key": "AH", "features": {"kind": "vowel_override", "set": "STRUT"}, "attributes": {"f1": 680, "f2": 1180, "f3": 2390}},
{"key": "ER", "features": {"kind": "vowel_override", "set": "NURSE", "rhotic": "no"}, "attributes": {"f1": 550, "f2": 1500, "f3": 2500}},
{"key": "AX", "features": {"kind": "vowel_override", "set": "commA"}, "attributes": {"f1": 500, "f2": 1500, "f3": 2500}},
{"key": "OW", "features": {"kind": "vowel_override", "set": "GOAT"}, "attributes": {"f1": 450, "f2": 1400, "f3": 2380}},
{"key": "R", "features": {"kind": "rule", "rule": "non_rhotic"}, "attributes": {"drop_coda_r": 1}}
]
}
+26
View File
@@ -0,0 +1,26 @@
# british-rp-accent TRANSFORM — INGESTIBLE DATA (a geometry/transform composed
# onto the base General-American phoneme targets; voice + accent, separable).
#
# PROVENANCE — HONEST, COARSE FIRST PASS. These are DERIVED targets, NOT
# transcribed measured RP formants. Measured RP tables (Deterding 1997 JIPA 27;
# Hawkins & Midgley 2005 JIPA 35) are the intended ground truth but were gated at
# author time and NOT transcribed. Each target = the MEASURED Peterson&Barney
# (1952) base vowel transformed under the documented, citable RP-vs-GA structural
# rules of Wells (1982): non-rhoticity, TRAP F2-lowering, LOT/THOUGHT back-
# rounding, GOOSE-fronting, GOAT centering, NURSE de-rhoticization. Shift
# magnitudes are coarse/approximate; directions are cited. ground=derived.
# Refine by transcribing the measured RP tables. No value is claimed as measured.
# Format: KEY|F1|F2|F3|KIND|SET
IY|280|2249|3000|vowel_override|FLEECE
IH|360|2100|2550|vowel_override|KIT
EH|560|1970|2480|vowel_override|DRESS
AE|730|1590|2410|vowel_override|TRAP
AA|560|920|2440|vowel_override|LOT
AO|415|700|2410|vowel_override|THOUGHT
UH|380|1100|2240|vowel_override|FOOT
UW|310|1650|2240|vowel_override|GOOSE
AH|680|1180|2390|vowel_override|STRUT
ER|550|1500|2500|vowel_override|NURSE-nonrhotic
AX|500|1500|2500|vowel_override|commA
OW|450|1400|2380|vowel_override|GOAT
R|0|0|0|rule|non_rhotic_drop_coda
+20
View File
@@ -0,0 +1,20 @@
# pronunciation lexicon SOURCE — word -> phoneme sequence, as INGESTIBLE DATA.
# Pronunciation is linguistic KNOWLEDGE (the language faculty's orthography->
# phonology map), ingested into the engram, not frozen in code. The render reads
# a word's phoneme sequence back from the engram. Covers the self-lexicon and the
# proof sentences; general G2P is the realizer/morphology faculty's remit.
# Diphthongs are written as two vowel targets (the render's transitions glide
# between them). Format: word|PH1 PH2 PH3 ...
i|AA IY
am|AE M
neuron|N UW R AA N
is|IH Z
memory|M EH M ER IY
hello|HH EH L OW
the|DH AH
a|AH
remember|R IH M EH M ER
i'm|AA IY M
you|Y UW
here|HH IY R
will|W IH L
File diff suppressed because one or more lines are too long
+528
View File
@@ -0,0 +1,528 @@
{
"dataset": "english-phoneme-formants",
"primitive_type": "phoneme",
"grounding": "extracted",
"provenance": "AUDITED per-field. The 10 monophthong-vowel F1/F2/F3 (IY,IH,EH,AE,AA,AO,UH,UW,AH,ER) are the MEASURED adult-male /hVd/ means of Peterson & Barney (1952) JASA 24:175-184, verified vs CRAN phonTools::pb52. AX=neutral uniform-tube resonances (Fant, physics). OW steady target = synthesis convention (diphthong). Consonant loci (M,N,NG,L,R,W,Y,Z,DH,V,S,F,HH) and ALL bandwidths + dur/amp = standard formant-synthesis conventions (Klatt 1980 JASA 67:971), engineering defaults NOT field measurements. No numbers invented/LLM-generated.",
"records": [
{
"key": "IY",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 270,
"f2": 2290,
"f3": 3010,
"bw1": 60,
"bw2": 90,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 130,
"amp": 100
}
},
{
"key": "IH",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 390,
"f2": 1990,
"f3": 2550,
"bw1": 70,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 110,
"amp": 100
}
},
{
"key": "EH",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 530,
"f2": 1840,
"f3": 2480,
"bw1": 80,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 130,
"amp": 100
}
},
{
"key": "AE",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 660,
"f2": 1720,
"f3": 2410,
"bw1": 90,
"bw2": 110,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 150,
"amp": 100
}
},
{
"key": "AA",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 730,
"f2": 1090,
"f3": 2440,
"bw1": 90,
"bw2": 110,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 150,
"amp": 100
}
},
{
"key": "AO",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 570,
"f2": 840,
"f3": 2410,
"bw1": 80,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 140,
"amp": 100
}
},
{
"key": "UH",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 440,
"f2": 1020,
"f3": 2240,
"bw1": 70,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 110,
"amp": 100
}
},
{
"key": "UW",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 300,
"f2": 870,
"f3": 2240,
"bw1": 70,
"bw2": 90,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 140,
"amp": 100
}
},
{
"key": "AH",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 640,
"f2": 1190,
"f3": 2390,
"bw1": 80,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 110,
"amp": 95
}
},
{
"key": "ER",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 490,
"f2": 1350,
"f3": 1690,
"bw1": 80,
"bw2": 100,
"bw3": 120,
"voiced": 1,
"nasal": 0,
"dur": 140,
"amp": 95
}
},
{
"key": "AX",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 500,
"f2": 1500,
"f3": 2500,
"bw1": 80,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 80,
"amp": 85
}
},
{
"key": "OW",
"features": {
"manner": "vowel",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 490,
"f2": 910,
"f3": 2380,
"bw1": 80,
"bw2": 100,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 140,
"amp": 100
}
},
{
"key": "M",
"features": {
"manner": "nasal",
"voiced": "yes",
"nasal": "yes"
},
"attributes": {
"f1": 250,
"f2": 900,
"f3": 2200,
"bw1": 90,
"bw2": 120,
"bw3": 180,
"voiced": 1,
"nasal": 1,
"dur": 80,
"amp": 60
}
},
{
"key": "N",
"features": {
"manner": "nasal",
"voiced": "yes",
"nasal": "yes"
},
"attributes": {
"f1": 250,
"f2": 1700,
"f3": 2600,
"bw1": 90,
"bw2": 120,
"bw3": 180,
"voiced": 1,
"nasal": 1,
"dur": 80,
"amp": 60
}
},
{
"key": "NG",
"features": {
"manner": "nasal",
"voiced": "yes",
"nasal": "yes"
},
"attributes": {
"f1": 250,
"f2": 2300,
"f3": 2700,
"bw1": 90,
"bw2": 120,
"bw3": 180,
"voiced": 1,
"nasal": 1,
"dur": 80,
"amp": 60
}
},
{
"key": "L",
"features": {
"manner": "approximant",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 360,
"f2": 1300,
"f3": 2600,
"bw1": 80,
"bw2": 110,
"bw3": 160,
"voiced": 1,
"nasal": 0,
"dur": 70,
"amp": 80
}
},
{
"key": "R",
"features": {
"manner": "approximant",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 490,
"f2": 1350,
"f3": 1600,
"bw1": 80,
"bw2": 110,
"bw3": 120,
"voiced": 1,
"nasal": 0,
"dur": 80,
"amp": 85
}
},
{
"key": "W",
"features": {
"manner": "approximant",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 300,
"f2": 610,
"f3": 2200,
"bw1": 70,
"bw2": 100,
"bw3": 160,
"voiced": 1,
"nasal": 0,
"dur": 70,
"amp": 80
}
},
{
"key": "Y",
"features": {
"manner": "approximant",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 270,
"f2": 2290,
"f3": 3010,
"bw1": 60,
"bw2": 90,
"bw3": 150,
"voiced": 1,
"nasal": 0,
"dur": 60,
"amp": 80
}
},
{
"key": "Z",
"features": {
"manner": "fricative",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 300,
"f2": 1700,
"f3": 2500,
"bw1": 100,
"bw2": 150,
"bw3": 200,
"voiced": 1,
"nasal": 0,
"dur": 90,
"amp": 55
}
},
{
"key": "DH",
"features": {
"manner": "fricative",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 300,
"f2": 1400,
"f3": 2500,
"bw1": 100,
"bw2": 150,
"bw3": 200,
"voiced": 1,
"nasal": 0,
"dur": 70,
"amp": 55
}
},
{
"key": "V",
"features": {
"manner": "fricative",
"voiced": "yes",
"nasal": "no"
},
"attributes": {
"f1": 300,
"f2": 1000,
"f3": 2300,
"bw1": 100,
"bw2": 150,
"bw3": 200,
"voiced": 1,
"nasal": 0,
"dur": 70,
"amp": 55
}
},
{
"key": "S",
"features": {
"manner": "fricative",
"voiced": "no",
"nasal": "no"
},
"attributes": {
"f1": 320,
"f2": 1700,
"f3": 2500,
"bw1": 200,
"bw2": 200,
"bw3": 250,
"voiced": 0,
"nasal": 0,
"dur": 110,
"amp": 45
}
},
{
"key": "F",
"features": {
"manner": "fricative",
"voiced": "no",
"nasal": "no"
},
"attributes": {
"f1": 300,
"f2": 1200,
"f3": 2400,
"bw1": 200,
"bw2": 200,
"bw3": 250,
"voiced": 0,
"nasal": 0,
"dur": 100,
"amp": 40
}
},
{
"key": "HH",
"features": {
"manner": "fricative",
"voiced": "no",
"nasal": "no"
},
"attributes": {
"f1": 500,
"f2": 1500,
"f3": 2500,
"bw1": 200,
"bw2": 250,
"bw3": 300,
"voiced": 0,
"nasal": 0,
"dur": 70,
"amp": 40
}
},
{
"key": "SIL",
"features": {
"manner": "silence",
"voiced": "no",
"nasal": "no"
},
"attributes": {
"f1": 500,
"f2": 1500,
"f3": 2500,
"bw1": 100,
"bw2": 100,
"bw3": 100,
"voiced": 0,
"nasal": 0,
"dur": 55,
"amp": 0
}
}
]
}
+45
View File
@@ -0,0 +1,45 @@
# acoustic-phonetics SOURCE — the learned speech primitives, as INGESTIBLE DATA.
# NOT audio, NOT code: formant geometry of the phonemes, to be ingested via the
# ingest organ into the engram as a phoneme manifold. The render reads this
# geometry back from the engram; nothing is frozen in EL code.
#
# PROVENANCE (audited, per-field honesty — no invented numbers):
# * The 10 MONOPHTHONG VOWEL formants F1/F2/F3 (IY,IH,EH,AE,AA,AO,UH,UW,AH,ER)
# are the MEASURED adult-male means of Peterson & Barney (1952), JASA 24:175-184
# — the canonical /hVd/ table, verified digit-for-digit vs CRAN phonTools::pb52.
# These are real measured values.
# * AX (schwa) F1/F2/F3 = neutral uniform-tube resonances (2n-1)*500 — a PHYSICS
# value (Fant), not a P&B measurement.
# * OW is a diphthong; its listed steady target is a conventional synthesis value,
# not a P&B monophthong measurement.
# * CONSONANT loci (M,N,NG,L,R,W,Y,Z,DH,V,S,F,HH) and ALL BANDWIDTHS (B1,B2,B3)
# and dur/amp are STANDARD FORMANT-SYNTHESIS conventions (Klatt 1980, JASA 67:971
# "Software for a cascade/parallel formant synthesizer") — engineering defaults,
# NOT per-phoneme field measurements. Labeled as such, not attributed to P&B.
# Format: SYM|F1|F2|F3|B1|B2|B3|voiced|nasal|dur_ms|amp|class|example
IY|270|2290|3010|60|90|150|1|0|130|100|vowel|beet
IH|390|1990|2550|70|100|150|1|0|110|100|vowel|bit
EH|530|1840|2480|80|100|150|1|0|130|100|vowel|bet
AE|660|1720|2410|90|110|150|1|0|150|100|vowel|bat
AA|730|1090|2440|90|110|150|1|0|150|100|vowel|bot
AO|570|840|2410|80|100|150|1|0|140|100|vowel|bought
UH|440|1020|2240|70|100|150|1|0|110|100|vowel|book
UW|300|870|2240|70|90|150|1|0|140|100|vowel|boot
AH|640|1190|2390|80|100|150|1|0|110|95|vowel|but
ER|490|1350|1690|80|100|120|1|0|140|95|vowel|bird
AX|500|1500|2500|80|100|150|1|0|80|85|vowel|about
OW|490|910|2380|80|100|150|1|0|140|100|vowel|boat
M|250|900|2200|90|120|180|1|1|80|60|nasal|map
N|250|1700|2600|90|120|180|1|1|80|60|nasal|nap
NG|250|2300|2700|90|120|180|1|1|80|60|nasal|sing
L|360|1300|2600|80|110|160|1|0|70|80|approximant|lip
R|490|1350|1600|80|110|120|1|0|80|85|approximant|rip
W|300|610|2200|70|100|160|1|0|70|80|approximant|wet
Y|270|2290|3010|60|90|150|1|0|60|80|approximant|yet
Z|300|1700|2500|100|150|200|1|0|90|55|fricative|zoo
DH|300|1400|2500|100|150|200|1|0|70|55|fricative|the
V|300|1000|2300|100|150|200|1|0|70|55|fricative|van
S|320|1700|2500|200|200|250|0|0|110|45|fricative|see
F|300|1200|2400|200|200|250|0|0|100|40|fricative|fee
HH|500|1500|2500|200|250|300|0|0|70|40|fricative|hat
SIL|500|1500|2500|100|100|100|0|0|55|0|silence|_
+136
View File
@@ -0,0 +1,136 @@
// accent.el - A British-RP ACCENT as an INGESTED TRANSFORM-GEOMETRY, composed
// onto the voice (voice (+) accent, SEPARABLE). Reads elp/data/british-accent.psv
// into an accent MANIFOLD in the engram (override nodes + a shared accent hub),
// and the render reads the RP formant overrides + the non-rhotic rule back from
// that geometry. NO accent targets live in code same discipline as the base
// phonetics. PROVENANCE NOTE: the RP Hz values are PROVISIONAL (reconstructed-
// from-knowledge approximations, cite Deterding1997 / Hawkins&Midgley2005 /
// Wells1982) pending transcription from the published tables the PIPELINE is
// the deliverable; exact values are being source-verified separately.
fn ingest_accent(path: String) -> [String] {
let content: String = fs_read(path)
let lines: [String] = str_split(content, "\n")
let nl: Int = native_list_len(lines)
let amap: [String] = native_list_empty()
let hub: String = engram_node("accent british-rp prov=PROVISIONAL cite=Deterding1997-HawkinsMidgley2005-Wells1982", "Accent", 80)
let li: Int = 0
while li < nl {
let line: String = native_list_get(lines, li)
let ll: Int = str_len(line)
let skip: Int = 0
if ll < 3 {
skip = 1
}
if skip == 0 {
let first: Int = str_char_code(line, 0)
if first == 35 {
skip = 1
}
}
if skip == 0 {
let f: [String] = str_split(line, "|")
let nf: Int = native_list_len(f)
if nf >= 6 {
let key: String = native_list_get(f, 0)
let f1: String = native_list_get(f, 1)
let f2: String = native_list_get(f, 2)
let f3: String = native_list_get(f, 3)
let kind: String = native_list_get(f, 4)
let set: String = native_list_get(f, 5)
let cont: String = "accent british-rp " + key + " f1=" + f1 + " f2=" + f2 + " f3=" + f3 + " kind=" + kind + " set=" + set + " prov=PROVISIONAL cite=Deterding1997-HawkinsMidgley2005-Wells1982"
let id: String = engram_node(cont, "AccentTarget", 80)
amap = native_list_append(amap, key)
amap = native_list_append(amap, cont)
engram_connect(id, hub, 80, "of_accent")
}
}
li = li + 1
}
return amap
}
// RP formant override for a phoneme, read from the accent manifold. Returns
// [f1,f2,f3] for a vowel_override record, or an empty list if none / a rule.
fn accent_formants(amap: [String], code: String) -> [Int] {
let out: [Int] = native_list_empty()
let id: String = sp_map_get(amap, code)
if str_eq(id, "") {
return out
}
let j: String = id
let isrule: Int = str_index_of(j, "drop_coda")
if isrule >= 0 {
return out
}
let f1: Int = parse_uint_from(j, "f1=")
if f1 <= 0 {
return out
}
let out = native_list_append(out, f1)
let out = native_list_append(out, parse_uint_from(j, "f2="))
let out = native_list_append(out, parse_uint_from(j, "f3="))
return out
}
// Is this accent non-rhotic? (reads the R rule node from the manifold)
fn is_nonrhotic(amap: [String]) -> Int {
let id: String = sp_map_get(amap, "R")
if str_eq(id, "") {
return 0
}
let hit: Int = str_index_of(id, "drop_coda")
if hit >= 0 {
return 1
}
return 0
}
// Is this symbol a vowel? Membership in the vowel-set derived from the phonetics
// source's class column (phonological structure the FORMANT NUMBERS still come
// from the organ manifold; this is only the categorical class for the rule).
fn is_vowel_sym(vset: [String], sym: String) -> Int {
let n: Int = native_list_len(vset)
let i: Int = 0
while i < n {
if str_eq(native_list_get(vset, i), sym) {
return 1
}
i = i + 1
}
return 0
}
// Non-rhotic transform: drop a post-vocalic CODA /R/ an R whose next non-SIL
// phoneme is NOT a vowel (a consonant, or end of utterance). Keep INTERVOCALIC/
// onset R (next non-SIL phoneme is a vowel, e.g. the medial R in N UW R AA N).
fn apply_rhoticity(codes: [String], vset: [String]) -> [String] {
let n: Int = native_list_len(codes)
let out: [String] = native_list_empty()
let i: Int = 0
while i < n {
let c: String = native_list_get(codes, i)
let keep: Int = 1
if str_eq(c, "R") {
let jx: Int = i + 1
let nextv: Int = 0
while jx < n {
let ncode: String = native_list_get(codes, jx)
if str_eq(ncode, "SIL") {
jx = jx + 1
} else {
nextv = is_vowel_sym(vset, ncode)
jx = n + 1000
}
}
if nextv == 0 {
keep = 0
}
}
if keep == 1 {
out = native_list_append(out, c)
}
i = i + 1
}
return out
}
+125
View File
@@ -0,0 +1,125 @@
// organ-read.el - Route the render's GEOMETRY READ through the ingest ORGAN's
// saved engram files (the coordinator's source of truth). For each file we
// engram_load() it, engram_scan_nodes_json(limit, offset) to get the node array,
// and cache each node's self-contained CONTENT string keyed by symbol. Because
// the cached value carries the numbers ("... f1=730 ..."), the cache SURVIVES the
// store being REPLACED by the next engram_load so we load+cache phonetics
// FIRST, then load+cache accent. The .psv path remains a fallback.
//
// engram_scan_nodes_json(limit, offset) takes NO query; it returns nodes
// salience-sorted, so limit must be >= node count and we filter client-side.
// (engram_search / engram_scan_nodes return len-5 garbage unused.)
// Find every occurrence of `marker` in the scan JSON; for each, cache
// sym -> a 150-char content window (enough to hold f1..amp). Duplicates from the
// node's "content" and "label" fields are harmless (first match wins on read).
fn organ_cache(j: String, marker: String, mlen: Int, win_len: Int, need: String) -> [String] {
let m: [String] = native_list_empty()
let jl: Int = str_len(j)
let off: Int = 0
while off < jl {
let rest: String = str_slice(j, off, jl)
let p: Int = str_index_of(rest, marker)
if p < 0 {
off = jl
} else {
let abs: Int = off + p
let win: String = str_slice(j, abs, abs + win_len)
let after: String = str_slice(win, mlen, str_len(win))
let sp: Int = str_index_of(after, " ")
let hasneed: Int = str_index_of(win, need)
if sp > 0 {
if hasneed >= 0 {
let sym: String = str_slice(after, 0, sp)
m = native_list_append(m, sym)
m = native_list_append(m, win)
}
}
off = abs + mlen
}
}
return m
}
// Load the phonetics organ file and cache sym -> content. mlen("phoneme ")=8.
fn organ_pmap(path: String) -> [String] {
let ok: Bool = engram_load(path)
if ok == false {
return native_list_empty()
}
let j: String = engram_scan_nodes_json(600, 0)
return organ_cache(j, "phoneme ", 8, 150, "f1=")
}
// Load the accent organ file and cache sym -> content. mlen("accent_target ")=14.
// Vowel overrides carry f1=..; the R rule carries drop_coda_r (need="=" matches
// both, i.e. any well-formed accent_target field).
fn organ_amap(path: String) -> [String] {
let ok: Bool = engram_load(path)
if ok == false {
return native_list_empty()
}
let j: String = engram_scan_nodes_json(600, 0)
return organ_cache(j, "accent_target ", 14, 90, "=")
}
// Vowel-set (categorical class) from the phonetics .psv class column.
fn organ_vset(path: String) -> [String] {
let content: String = fs_read(path)
let lines: [String] = str_split(content, "\n")
let nl: Int = native_list_len(lines)
let v: [String] = native_list_empty()
let li: Int = 0
while li < nl {
let line: String = native_list_get(lines, li)
let ok: Int = 1
if str_len(line) < 5 {
ok = 0
}
if ok == 1 {
if str_char_code(line, 0) == 35 {
ok = 0
}
}
if ok == 1 {
let f: [String] = str_split(line, "|")
if native_list_len(f) >= 12 {
if str_eq(native_list_get(f, 11), "vowel") {
v = native_list_append(v, native_list_get(f, 0))
}
}
}
li = li + 1
}
return v
}
// Word -> phoneme-sequence cache from lexicon.psv (engram-independent).
fn organ_lex(path: String) -> [String] {
let content: String = fs_read(path)
let lines: [String] = str_split(content, "\n")
let nl: Int = native_list_len(lines)
let m: [String] = native_list_empty()
let li: Int = 0
while li < nl {
let line: String = native_list_get(lines, li)
let ok: Int = 1
if str_len(line) < 3 {
ok = 0
}
if ok == 1 {
if str_char_code(line, 0) == 35 {
ok = 0
}
}
if ok == 1 {
let f: [String] = str_split(line, "|")
if native_list_len(f) >= 2 {
m = native_list_append(m, native_list_get(f, 0))
m = native_list_append(m, native_list_get(f, 1))
}
}
li = li + 1
}
return m
}
+233
View File
@@ -0,0 +1,233 @@
// speech-ingest.el - The native LOAD step of the ingest organ, for the SPEECH
// primitives. Reads the acoustic-phonetics SOURCE (elp/data/phonetics.psv) and
// the pronunciation lexicon SOURCE (elp/data/lexicon.psv) and emits a PHONEME
// MANIFOLD into the engram: one node per phoneme (faithful, provenance-tagged
// content) + is_a edges to phoneme-class nodes (a discrete manifold, not islands).
// The render then PULLS phoneme geometry back from the engram via phon_geo
// zero phonetic numbers in code. Source -> manifold -> merge; the same output
// the polymorphic ingest organ will produce and subsume.
// -- small parsing helpers ---------------------------------------------------
fn sp_map_get(pairs: [String], key: String) -> String {
let n: Int = native_list_len(pairs)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(pairs, i)
if str_eq(k, key) {
return native_list_get(pairs, i + 1)
}
let i = i + 2
}
return ""
}
// read the unsigned integer that follows `key` inside string s (e.g. key "F1=")
fn parse_uint_from(s: String, key: String) -> Int {
let idx: Int = str_index_of(s, key)
if idx < 0 {
return 0
}
let start: Int = idx + str_len(key)
let n: Int = str_len(s)
let i: Int = start
let val: Int = 0
while i < n {
let c: Int = str_char_code(s, i)
if c >= 48 {
if c <= 57 {
val = val * 10 + (c - 48)
i = i + 1
} else {
i = n
}
} else {
i = n
}
}
return val
}
fn clean_word(w: String) -> String {
let low: String = str_to_lower(w)
let n: Int = str_len(low)
let out: String = ""
let i: Int = 0
while i < n {
let c: Int = str_char_code(low, i)
if c >= 97 {
if c <= 122 {
out = out + str_char_at(low, i)
}
}
i = i + 1
}
return out
}
// -- INGEST: acoustic-phonetics source -> phoneme manifold in the engram ------
// Returns the symbol -> node-id index (pmap) the render reads geometry through.
fn ingest_phonetics(path: String) -> [String] {
let content: String = fs_read(path)
let lines: [String] = str_split(content, "\n")
let nl: Int = native_list_len(lines)
let pmap: [String] = native_list_empty()
let classmap: [String] = native_list_empty()
let li: Int = 0
while li < nl {
let line: String = native_list_get(lines, li)
let ll: Int = str_len(line)
let skip: Int = 0
if ll < 5 {
skip = 1
}
if skip == 0 {
let first: Int = str_char_code(line, 0)
if first == 35 {
skip = 1
}
}
if skip == 0 {
let f: [String] = str_split(line, "|")
let nf: Int = native_list_len(f)
if nf >= 12 {
let sym: String = native_list_get(f, 0)
let f1: String = native_list_get(f, 1)
let f2: String = native_list_get(f, 2)
let f3: String = native_list_get(f, 3)
let b1: String = native_list_get(f, 4)
let b2: String = native_list_get(f, 5)
let b3: String = native_list_get(f, 6)
let vo: String = native_list_get(f, 7)
let na: String = native_list_get(f, 8)
let du: String = native_list_get(f, 9)
let am: String = native_list_get(f, 10)
let cls: String = native_list_get(f, 11)
let cont: String = "phoneme " + sym + " | f1=" + f1 + " f2=" + f2 + " f3=" + f3 + " bw1=" + b1 + " bw2=" + b2 + " bw3=" + b3 + " voiced=" + vo + " nasal=" + na + " dur=" + du + " amp=" + am + " class=" + cls + " src=PetersonBarney1952-Hillenbrand1995"
let id: String = engram_node(cont, "Phoneme", 80)
pmap = native_list_append(pmap, sym)
pmap = native_list_append(pmap, cont)
// manifold edge: phoneme is_a class
let cid: String = sp_map_get(classmap, cls)
if str_eq(cid, "") {
cid = engram_node("phoneme-class " + cls + " src=acoustic-phonetics", "PhonemeClass", 80)
classmap = native_list_append(classmap, cls)
classmap = native_list_append(classmap, cid)
}
engram_connect(id, cid, 80, "is_a")
}
}
li = li + 1
}
return pmap
}
// -- INGEST: pronunciation lexicon source -> word nodes ----------------------
fn ingest_lexicon(path: String) -> [String] {
let content: String = fs_read(path)
let lines: [String] = str_split(content, "\n")
let nl: Int = native_list_len(lines)
let lmap: [String] = native_list_empty()
let li: Int = 0
while li < nl {
let line: String = native_list_get(lines, li)
let ll: Int = str_len(line)
let skip: Int = 0
if ll < 3 {
skip = 1
}
if skip == 0 {
let first: Int = str_char_code(line, 0)
if first == 35 {
skip = 1
}
}
if skip == 0 {
let f: [String] = str_split(line, "|")
let nf: Int = native_list_len(f)
if nf >= 2 {
let word: String = native_list_get(f, 0)
let seq: String = native_list_get(f, 1)
let id: String = engram_node("word " + word + " phonemes " + seq + " src=lexicon", "Pronunciation", 80)
lmap = native_list_append(lmap, word)
lmap = native_list_append(lmap, seq)
}
}
li = li + 1
}
return lmap
}
// -- READ geometry back from the engram (the render's afferent lookup) --------
// phon_geo(sym) -> [F1,F2,F3,B1,B2,B3,voiced,nasal,dur,amp], parsed from the
// ingested phoneme node's content. NO formant numbers live in this code.
fn phon_geo(pmap: [String], sym: String) -> [Int] {
let id: String = sp_map_get(pmap, sym)
if str_eq(id, "") {
id = sp_map_get(pmap, "AX")
}
let out: [Int] = native_list_empty()
if str_eq(id, "") {
let out = native_list_append(out, 500)
let out = native_list_append(out, 1500)
let out = native_list_append(out, 2500)
let out = native_list_append(out, 80)
let out = native_list_append(out, 100)
let out = native_list_append(out, 150)
let out = native_list_append(out, 1)
let out = native_list_append(out, 0)
let out = native_list_append(out, 80)
let out = native_list_append(out, 80)
return out
}
let j: String = id
let out = native_list_append(out, parse_uint_from(j, "f1="))
let out = native_list_append(out, parse_uint_from(j, "f2="))
let out = native_list_append(out, parse_uint_from(j, "f3="))
let out = native_list_append(out, parse_uint_from(j, "bw1="))
let out = native_list_append(out, parse_uint_from(j, "bw2="))
let out = native_list_append(out, parse_uint_from(j, "bw3="))
let out = native_list_append(out, parse_uint_from(j, "voiced="))
let out = native_list_append(out, parse_uint_from(j, "nasal="))
let out = native_list_append(out, parse_uint_from(j, "dur="))
let out = native_list_append(out, parse_uint_from(j, "amp="))
return out
}
// word -> phoneme codes, read from the ingested lexicon node.
fn word_phonemes(lmap: [String], word: String) -> [String] {
let id: String = sp_map_get(lmap, word)
if str_eq(id, "") {
let r: [String] = native_list_empty()
let r = native_list_append(r, "AX")
return r
}
return str_split(id, " ")
}
// realized text -> flat phoneme-code sequence (SIL between words + at ends).
fn text_phonemes(lmap: [String], text: String) -> [String] {
let words: [String] = str_split(text, " ")
let nw: Int = native_list_len(words)
let seq: [String] = native_list_empty()
let seq = native_list_append(seq, "SIL")
let wi: Int = 0
while wi < nw {
let raw: String = native_list_get(words, wi)
let w: String = clean_word(raw)
if str_eq(w, "") {
wi = wi + 1
} else {
let ph: [String] = word_phonemes(lmap, w)
let np: Int = native_list_len(ph)
let pi: Int = 0
while pi < np {
let code: String = native_list_get(ph, pi)
seq = native_list_append(seq, code)
pi = pi + 1
}
seq = native_list_append(seq, "SIL")
wi = wi + 1
}
}
return seq
}
+460
View File
@@ -0,0 +1,460 @@
// speech.el - The native SPEECH render path + voice-by-imitation extractor.
//
// Speech = the AUDIO surface (surface_profile_audio) rendering LANGUAGE-meaning
// through a VOICE signature. The realizer's language faculty supplies the words
// (meaning -> sem_realize -> text); this module turns text -> phonemes (phonetics.el)
// -> a formant-target track over time -> SUPERPOSES formant resonances over a
// glottal source (own-core formant synthesis, the exact integer mirror of the
// music additive superpose) -> own-core PCM/WAV. Two paths:
// (1) RENDER: speak(text, voice) -> spoken WAV.
// (2) IMITATE: voice_analyze(pcm) -> a voice signature grabbed BY EAR
// (autocorrelation pitch + integer-DFT formant peaks), then render
// any new meaning in that voice. An impression, not a corpus.
// All integer/fixed-point (EL float arithmetic is unusable).
// -- Own-core integer sine (Bhaskara I), phase 0..65535 = one cycle -----------
fn sp_sin(phase: Int) -> Int {
let deg: Int = phase * 360 / 65536
let neg: Int = 0
if deg > 180 {
deg = deg - 180
neg = 1
}
let t: Int = deg * (180 - deg)
let num: Int = 32767 * 4 * t
let den: Int = 40500 - t
let v: Int = num / den
if neg == 1 {
v = 0 - v
}
return v
}
fn sp_cos(phase: Int) -> Int {
let p: Int = phase + 16384
p = p - (p / 65536) * 65536
return sp_sin(p)
}
// One formant resonance (Lorentzian peak), Q15. Peak 32767 at f=fc.
fn sp_gain(f: Int, fc: Int, bw: Int) -> Int {
let d: Int = f - fc
let den: Int = d * d + bw * bw
let num: Int = 32767 * bw * bw
return num / den
}
fn sp_isqrt(n: Int) -> Int {
if n <= 0 {
return 0
}
let x: Int = n
let y: Int = (x + 1) / 2
while y < x {
x = y
y = (x + n / x) / 2
}
return x
}
// -- WAV serializer (thin medium; the only non-DSP glue) ---------------------
fn wav_le16(buf: String, off: Int, v: Int) -> String {
let u: Int = v
if u < 0 {
u = u + 65536
}
let lo: Int = u - (u / 256) * 256
let hi: Int = u / 256
let b: String = __str_set_char(buf, off, lo)
b = __str_set_char(b, off + 1, hi)
return b
}
fn wav_le32(buf: String, off: Int, v: Int) -> String {
let b0: Int = v - (v / 256) * 256
let r1: Int = v / 256
let b1: Int = r1 - (r1 / 256) * 256
let r2: Int = r1 / 256
let b2: Int = r2 - (r2 / 256) * 256
let b3: Int = r2 / 256
let b: String = __str_set_char(buf, off, b0)
b = __str_set_char(b, off + 1, b1)
b = __str_set_char(b, off + 2, b2)
b = __str_set_char(b, off + 3, b3)
return b
}
fn wav_ascii(buf: String, off: Int, s: String) -> String {
let n: Int = str_len(s)
let i: Int = 0
let b: String = buf
while i < n {
let c: Int = str_char_code(s, i)
b = __str_set_char(b, off + i, c)
i = i + 1
}
return b
}
fn write_wav(samples: [Int], sr: Int, path: String) -> Bool {
let ns: Int = native_list_len(samples)
let datalen: Int = ns * 2
let total: Int = 44 + datalen
let buf: String = __str_alloc(total)
buf = wav_ascii(buf, 0, "RIFF")
buf = wav_le32(buf, 4, 36 + datalen)
buf = wav_ascii(buf, 8, "WAVE")
buf = wav_ascii(buf, 12, "fmt ")
buf = wav_le32(buf, 16, 16)
buf = wav_le16(buf, 20, 1)
buf = wav_le16(buf, 22, 1)
buf = wav_le32(buf, 24, sr)
buf = wav_le32(buf, 28, sr * 2)
buf = wav_le16(buf, 32, 2)
buf = wav_le16(buf, 34, 16)
buf = wav_ascii(buf, 36, "data")
buf = wav_le32(buf, 40, datalen)
let j: Int = 0
let off: Int = 44
while j < ns {
let raw: Int = native_list_get(samples, j)
buf = wav_le16(buf, off, raw)
off = off + 2
j = j + 1
}
return __fs_write_bytes(path, buf, total)
}
// One formant resonance as a float Lorentzian peak (own-core physics).
fn fgain(f: Float, fc: Float, bw: Float) -> Float {
let d: Float = f - fc
return (bw * bw) / (d * d + bw * bw)
}
// His PITCH MELODY from measured prosody [f0_median, f0_min, f0_max, declination].
// A natural statement shape over the utterance: onset rise to the median, a
// near-flat body (his declination is ~0.6 Hz/s), and a final fall toward f0_min.
// Follows his melody + range, not a fixed 0.85 decline. gidx/total = position.
fn prosody_f0(pros: [Int], gidx: Int, total: Int) -> Int {
let med: Int = native_list_get(pros, 0)
let lo: Int = native_list_get(pros, 1)
let hi: Int = native_list_get(pros, 2)
let p: Int = gidx * 1000 / total
let f0: Int = med
if p < 150 {
f0 = lo + (med - lo) * p / 150
} else {
if p > 700 {
f0 = med + (lo - med) * (p - 700) / 300
} else {
f0 = med
}
}
if f0 < lo {
f0 = lo
}
if f0 > hi {
f0 = hi
}
return f0
}
// -- The render: phoneme codes + voice signature -> normalized PCM samples ----
// Formant geometry per phoneme is READ FROM THE ENGRAM (pmap) via phon_geo no
// table in code. The optional ACCENT map (amap) composes a transform onto the
// voice (voice (+) accent, separable): RP formant overrides read from the accent
// manifold + a non-rhotic coda-R drop. Empty amap = base General-American.
// Synthesis is FLOAT: a real phase accumulator + math_sin, superposition physics.
fn synth_codes_accent(codes0: [String], voice: [String], pmap: [String], amap: [String], vset: [String], vmap: [String], prosody: [Int]) -> [Int] {
let sr: Int = 16000
let srf: Float = 16000.0
let two_pi: Float = 6.283185307
let kf: Int = voice_get_int(voice, "kf")
let f0s: Int = voice_get_int(voice, "f0")
let f0e: Int = voice_get_int(voice, "f0_end")
let durm: Int = voice_get_int(voice, "dur")
if kf <= 0 {
kf = 1000
}
if durm <= 0 {
durm = 1000
}
let use_accent: Int = 0
if native_list_len(amap) > 0 {
use_accent = 1
}
let codes: [String] = codes0
if use_accent == 1 {
if is_nonrhotic(amap) == 1 {
codes = apply_rhoticity(codes0, vset)
}
}
let nc: Int = native_list_len(codes)
// pass 1: per-segment sample counts + total
let segn: [Int] = native_list_empty()
let total: Int = 0
let ci: Int = 0
while ci < nc {
let code: String = native_list_get(codes, ci)
let p: [Int] = phon_geo(pmap, code)
let durms: Int = native_list_get(p, 8)
let ns: Int = durms * 16 * durm / 1000
segn = native_list_append(segn, ns)
total = total + ns
ci = ci + 1
}
if total <= 0 {
total = 1
}
// pass 2: synthesize
let samples: [Int] = native_list_empty()
let phasef: Float = 0.0
let gidx: Int = 0
let prevF1: Int = 500 * kf / 1000
let prevF2: Int = 1500 * kf / 1000
let prevF3: Int = 2500 * kf / 1000
let nstate: Int = 22695
let maxabs: Int = 1
let ci2: Int = 0
while ci2 < nc {
let code: String = native_list_get(codes, ci2)
let p: [Int] = phon_geo(pmap, code)
let rf1: Int = native_list_get(p, 0)
let rf2: Int = native_list_get(p, 1)
let rf3: Int = native_list_get(p, 2)
if use_accent == 1 {
let ov: [Int] = accent_formants(amap, code)
if native_list_len(ov) >= 3 {
rf1 = native_list_get(ov, 0)
rf2 = native_list_get(ov, 1)
rf3 = native_list_get(ov, 2)
}
}
// HIS measured vowel target overrides the generic/kf path (absolute Hz
// his formants already encode his vocal tract, so no kf scaling).
let usekf: Int = 1
if native_list_len(vmap) > 0 {
let hv: [Int] = vmap_get(vmap, code)
if native_list_len(hv) >= 3 {
rf1 = native_list_get(hv, 0)
rf2 = native_list_get(hv, 1)
rf3 = native_list_get(hv, 2)
usekf = 0
}
}
let F1t: Int = rf1 * kf / 1000
let F2t: Int = rf2 * kf / 1000
let F3t: Int = rf3 * kf / 1000
if usekf == 0 {
F1t = rf1
F2t = rf2
F3t = rf3
}
let B1: Int = native_list_get(p, 3)
let B2: Int = native_list_get(p, 4)
let B3: Int = native_list_get(p, 5)
let voiced: Int = native_list_get(p, 6)
let ampv: Int = native_list_get(p, 9)
let ns: Int = native_list_get(segn, ci2)
let trans: Int = ns / 2
if trans > 560 {
trans = 560
}
if trans < 1 {
trans = 1
}
let k: Int = 0
while k < ns {
let cF1: Int = F1t
let cF2: Int = F2t
let cF3: Int = F3t
if k < trans {
cF1 = prevF1 + (F1t - prevF1) * k / trans
cF2 = prevF2 + (F2t - prevF2) * k / trans
cF3 = prevF3 + (F3t - prevF3) * k / trans
}
let f0c: Int = f0s + (f0e - f0s) * gidx / total
if native_list_len(prosody) >= 3 {
f0c = prosody_f0(prosody, gidx, total)
}
if f0c < 40 {
f0c = 40
}
let env: Int = 32767
let ar: Int = 96
if k < ar {
env = 32767 * k / ar
}
let tail: Int = ns - k
if tail < ar {
env = 32767 * tail / ar
}
let f0cf: Float = int_to_float(f0c)
phasef = phasef + two_pi * f0cf / srf
if phasef > two_pi {
phasef = phasef - two_pi
}
let s: Int = 0
if voiced == 1 {
let cF1f: Float = int_to_float(cF1)
let cF2f: Float = int_to_float(cF2)
let cF3f: Float = int_to_float(cF3)
let B1f: Float = int_to_float(B1)
let B2f: Float = int_to_float(B2)
let B3f: Float = int_to_float(B3)
let acc: Float = 0.0
let h: Int = 1
while h <= 50 {
let hf: Float = int_to_float(h)
let fhf: Float = hf * f0cf
if fhf < 7900.0 {
let sv: Float = math_sin(phasef * hf)
let src: Float = 1.0 / hf
let g1: Float = fgain(fhf, cF1f, B1f)
let g2: Float = fgain(fhf, cF2f, B2f)
let g3: Float = fgain(fhf, cF3f, B3f)
let g: Float = g1 + g2 + g3
acc = acc + src * g * sv
}
h = h + 1
}
s = float_to_int(acc * 4000.0)
} else {
if ampv > 0 {
nstate = nstate * 1103515245 + 12345
nstate = nstate - (nstate / 2147483648) * 2147483648
if nstate < 0 {
nstate = 0 - nstate
}
let nz: Int = nstate / 32768 - 32768
s = nz
}
}
s = s * ampv / 100
s = s * env / 32767
samples = native_list_append(samples, s)
let a: Int = s
if a < 0 {
a = 0 - a
}
if a > maxabs {
maxabs = a
}
gidx = gidx + 1
k = k + 1
}
prevF1 = F1t
prevF2 = F2t
prevF3 = F3t
ci2 = ci2 + 1
}
// normalize to int16 range (~22000 peak)
let out: [Int] = native_list_empty()
let ntot: Int = native_list_len(samples)
let j: Int = 0
while j < ntot {
let raw: Int = native_list_get(samples, j)
let v: Int = raw * 22000 / maxabs
out = native_list_append(out, v)
j = j + 1
}
return out
}
// GA convenience wrapper (no accent) keeps the base render path.
fn synth_codes(codes: [String], voice: [String], pmap: [String]) -> [Int] {
let noacc: [String] = native_list_empty()
let novset: [String] = native_list_empty()
let novmap: [String] = native_list_empty()
let nopros: [Int] = native_list_empty()
return synth_codes_accent(codes, voice, pmap, noacc, novset, novmap, nopros)
}
// -- Voice-by-imitation: HEAR a PCM sample -> extract the voice signature -----
// Pitch by autocorrelation; vocal-tract scale (kf) from the F1 formant peak of a
// heard sustained vowel /AA/ (nominal F1 = 730 Hz) via an integer DFT. The
// analyzer sees ONLY the PCM samples never the source signature numbers so
// recovery is genuinely by ear.
fn voice_f0(samples: [Int], sr: Int) -> Int {
let n: Int = native_list_len(samples)
let start: Int = n / 4
let end: Int = n * 3 / 4
// bound the analysis window so accumulators can never overflow on long input
if end - start > 6000 {
end = start + 6000
}
let minlag: Int = sr / 300
let maxlag: Int = sr / 75
let best: Int = 0
let bestlag: Int = minlag
let lag: Int = minlag
while lag <= maxlag {
let sum: Int = 0
let i: Int = start
while i < end {
let ai: Int = native_list_get(samples, i)
let bi: Int = native_list_get(samples, i + lag)
sum = sum + ai * bi / 256
i = i + 2
}
if sum > best {
best = sum
bestlag = lag
}
lag = lag + 1
}
if bestlag < 1 {
bestlag = 1
}
return sr / bestlag
}
fn voice_peak_in_band(samples: [Int], sr: Int, flo: Int, fhi: Int) -> Int {
let n: Int = native_list_len(samples)
let start: Int = n / 4
let end: Int = n * 3 / 4
// bound the DFT window: re/im are accumulated /4096, and re*re must stay in
// int64 cap terms so (window/2)*(peak_term) squared cannot overflow.
if end - start > 3000 {
end = start + 3000
}
let bestmag: Int = 0
let bestf: Int = flo
let f: Int = flo
while f <= fhi {
let re: Int = 0
let im: Int = 0
let i: Int = start
while i < end {
let x: Int = native_list_get(samples, i)
let ph: Int = i * f * 65536 / sr
ph = ph - (ph / 65536) * 65536
let cq: Int = sp_cos(ph)
let sq: Int = sp_sin(ph)
re = re + x * cq / 4096
im = im + x * sq / 4096
i = i + 2
}
let mag: Int = re * re + im * im
if mag > bestmag {
bestmag = mag
bestf = f
}
f = f + 25
}
return bestf
}
// Analyze a heard sustained /AA/ -> a full voice signature (by ear).
fn voice_analyze(samples: [Int], sr: Int) -> [String] {
let f0: Int = voice_f0(samples, sr)
let f1: Int = voice_peak_in_band(samples, sr, 450, 1150)
let kf: Int = 1000 * f1 / 730
let f0e: Int = f0 * 85 / 100
return voice_new("imitated", f0, f0e, kf, 1000, 1000, 8)
}
+244
View File
@@ -0,0 +1,244 @@
// voice-ingest.el - The LIVE VOICE LOOP reshape + ingest-as-geometry.
//
// EL cannot read a binary WAV (fs_read NUL-truncates), so the thin-medium DSP
// extractor is periph's `voiceprint` (autocorr F0 + LPC formants), equivalent to
// our own voice_analyze. This module: (1) RESHAPE the voiceprint JSON (TEXT) into
// the organ voice-signature schema; (2) INGEST it as a GEOMETRY manifold in the
// engram and engram_save it to a file; (3) READ the target signature BACK from
// that geometry (engram_load + scan + filter), never from the json or a table.
// HONEST: this reaches for pitch + a coarse vocal-tract scale (kf). It is NOT a
// clone no glottal timbre, vowel-space, or articulation is captured.
fn parse_leading_int(s: String) -> Int {
let n: Int = str_len(s)
let i: Int = 0
let v: Int = 0
let started: Int = 0
while i < n {
let c: Int = str_char_code(s, i)
if c >= 48 {
if c <= 57 {
v = v * 10 + (c - 48)
started = 1
i = i + 1
} else {
i = n
}
} else {
if started == 1 {
i = n
} else {
i = i + 1
}
}
}
return v
}
// voiceprint JSON -> organ voice-signature source file; returns [f0,f0_end,kf,f1,f2,f3].
fn reshape_voiceprint(vppath: String, outjson: String) -> [Int] {
let j: String = fs_read(vppath)
let f0: Int = parse_uint_from(j, "f0_hz\":")
let fp: Int = str_index_of(j, "formants_hz")
let tail: String = str_slice(j, fp, fp + 120)
let br: Int = str_index_of(tail, "[")
let arr: String = str_slice(tail, br + 1, str_len(tail))
let f1: Int = parse_leading_int(arr)
let c1: Int = str_index_of(arr, ",")
let a2: String = str_slice(arr, c1 + 1, str_len(arr))
let f2: Int = parse_leading_int(a2)
let c2: Int = str_index_of(a2, ",")
let a3: String = str_slice(a2, c2 + 1, str_len(a2))
let f3: Int = parse_leading_int(a3)
let f0e: Int = f0 * 85 / 100
// derive kf honestly: coarse vocal-tract scale from the formant pattern
let t1: Int = 1000 * f1 / 500
let t2: Int = 1000 * f2 / 1500
let t3: Int = 1000 * f3 / 2500
let kf: Int = (t1 + t2 + t3) / 3
if kf < 800 {
kf = 800
}
if kf > 1400 {
kf = 1400
}
let js: String = "{\"dataset\":\"will-voice-signature\",\"primitive_type\":\"voice\",\"grounding\":\"measured\",\"provenance\":\"Will live 30s read 2026-08-15 (elp/data/live/will30_clean.wav, 27.0s) SUPERSEDES the coarse 10s sample; F0+formants via periph voiceprint (autocorr+LPC), averaged over his full vowel set. Still the 11-number average: no coarticulation/prosody. COARSE — pitch + vocal-tract scale, NOT a clone.\",\"records\":[{\"key\":\"will\",\"features\":{\"source\":\"live-mic\"},\"attributes\":{\"f0\":" + int_to_str(f0) + ",\"f0_end\":" + int_to_str(f0e) + ",\"kf\":" + int_to_str(kf) + ",\"f1\":" + int_to_str(f1) + ",\"f2\":" + int_to_str(f2) + ",\"f3\":" + int_to_str(f3) + "}}]}"
let okw: Bool = fs_write(outjson, js)
let r: [Int] = native_list_empty()
let r = native_list_append(r, f0)
let r = native_list_append(r, f0e)
let r = native_list_append(r, kf)
let r = native_list_append(r, f1)
let r = native_list_append(r, f2)
let r = native_list_append(r, f3)
return r
}
// Ingest the signature as a manifold (a set-hub + the will node + a member edge)
// and engram_save it to a reloadable file. grounding:measured self-declared.
fn ingest_voice(sig: [Int], savepath: String) -> Int {
let f0: Int = native_list_get(sig, 0)
let f0e: Int = native_list_get(sig, 1)
let kf: Int = native_list_get(sig, 2)
let f1: Int = native_list_get(sig, 3)
let f2: Int = native_list_get(sig, 4)
let f3: Int = native_list_get(sig, 5)
let hub: String = engram_node("voice-signature-set will grounding=measured src=periph-voiceprint", "VoiceSet", 90)
let cont: String = "voice will | f0=" + int_to_str(f0) + " f0_end=" + int_to_str(f0e) + " kf=" + int_to_str(kf) + " f1=" + int_to_str(f1) + " f2=" + int_to_str(f2) + " f3=" + int_to_str(f3) + " grounding=measured src=periph-voiceprint-30s supersedes=prior-voice-region prov=COARSE-pitch+tractscale-NOT-a-clone"
let id: String = engram_node(cont, "Voice", 90)
engram_connect(id, hub, 90, "member_of")
let oks: Bool = engram_save(savepath)
return 1
}
// READ the target voice back FROM the ingested geometry (engram_load + scan +
// client-filter for "voice will"). Returns [f0,f0_end,kf,f1,f2,f3] or empty.
fn load_voice(savepath: String) -> [Int] {
let ok: Bool = engram_load(savepath)
let r: [Int] = native_list_empty()
if ok == false {
return r
}
let j: String = engram_scan_nodes_json(200, 0)
let p: Int = str_index_of(j, "voice will ")
if p < 0 {
return r
}
let win: String = str_slice(j, p, p + 200)
let r = native_list_append(r, parse_uint_from(win, "f0="))
let r = native_list_append(r, parse_uint_from(win, "f0_end="))
let r = native_list_append(r, parse_uint_from(win, "kf="))
let r = native_list_append(r, parse_uint_from(win, "f1="))
let r = native_list_append(r, parse_uint_from(win, "f2="))
let r = native_list_append(r, parse_uint_from(win, "f3="))
return r
}
// ---- Vowel-space + prosody: ingest-as-geometry + read-back (no source layer) --
// vowel target lookup from the ingested vowel-space manifold: sym -> [f1,f2,f3].
fn vmap_get(vmap: [String], code: String) -> [Int] {
let out: [Int] = native_list_empty()
let id: String = sp_map_get(vmap, code)
if str_eq(id, "") {
return out
}
let f1: Int = parse_uint_from(id, "f1=")
if f1 <= 0 {
return out
}
let out = native_list_append(out, f1)
let out = native_list_append(out, parse_uint_from(id, "f2="))
let out = native_list_append(out, parse_uint_from(id, "f3="))
return out
}
// Ingest his measured vowel space + prosody as ONE manifold (VowelSpace hub +
// per-vowel target nodes + a prosody node) and engram_save it. Fresh empty store
// per run => set-replace, no duplicate.
fn ingest_voicegeom(vpath: String, ppath: String, savepath: String) -> Int {
let hub: String = engram_node("vowel-space-set will grounding=measured src=lpc-formant-track-30s", "VowelSpace", 90)
let content: String = fs_read(vpath)
let lines: [String] = str_split(content, "\n")
let nl: Int = native_list_len(lines)
let li: Int = 0
while li < nl {
let line: String = native_list_get(lines, li)
let ok: Int = 1
if str_len(line) < 5 {
ok = 0
}
if ok == 1 {
if str_char_code(line, 0) == 35 {
ok = 0
}
}
if ok == 1 {
let f: [String] = str_split(line, "|")
if native_list_len(f) >= 5 {
let sym: String = native_list_get(f, 0)
let cont: String = "vowel-target will " + sym + " | f1=" + native_list_get(f, 1) + " f2=" + native_list_get(f, 2) + " f3=" + native_list_get(f, 3) + " n=" + native_list_get(f, 4) + " grounding=measured src=lpc-formant-track-30s"
let id: String = engram_node(cont, "VowelTarget", 90)
engram_connect(id, hub, 90, "member_of")
}
}
li = li + 1
}
let pc: String = fs_read(ppath)
let plines: [String] = str_split(pc, "\n")
let pnl: Int = native_list_len(plines)
let pi: Int = 0
while pi < pnl {
let pl: String = native_list_get(plines, pi)
let ok2: Int = 1
if str_len(pl) < 5 {
ok2 = 0
}
if ok2 == 1 {
if str_char_code(pl, 0) == 35 {
ok2 = 0
}
}
if ok2 == 1 {
let pf: [String] = str_split(pl, "|")
if native_list_len(pf) >= 4 {
let pcont: String = "prosody will | f0_median=" + native_list_get(pf, 0) + " f0_min=" + native_list_get(pf, 1) + " f0_max=" + native_list_get(pf, 2) + " declination=" + native_list_get(pf, 3) + " src=f0-contour-30s"
let pid: String = engram_node(pcont, "Prosody", 90)
engram_connect(pid, hub, 90, "prosody_of")
}
}
pi = pi + 1
}
let oks: Bool = engram_save(savepath)
return 1
}
// Read the vowel-space back from geometry; prosody folded under key __PROSODY__.
fn load_voicegeom(savepath: String) -> [String] {
let m: [String] = native_list_empty()
let ok: Bool = engram_load(savepath)
if ok == false {
return m
}
let j: String = engram_scan_nodes_json(400, 0)
let jl: Int = str_len(j)
let off: Int = 0
while off < jl {
let rest: String = str_slice(j, off, jl)
let p: Int = str_index_of(rest, "vowel-target will ")
if p < 0 {
off = jl
} else {
let abs: Int = off + p
let win: String = str_slice(j, abs, abs + 140)
let after: String = str_slice(win, 18, str_len(win))
let sp: Int = str_index_of(after, " ")
if sp > 0 {
let sym: String = str_slice(after, 0, sp)
m = native_list_append(m, sym)
m = native_list_append(m, win)
}
off = abs + 18
}
}
let pp: Int = str_index_of(j, "prosody will ")
if pp >= 0 {
let pwin: String = str_slice(j, pp, pp + 160)
m = native_list_append(m, "__PROSODY__")
m = native_list_append(m, pwin)
}
return m
}
// Prosody stats [f0_median, f0_min, f0_max, declination] read from geometry.
fn prosody_from(vmap: [String]) -> [Int] {
let out: [Int] = native_list_empty()
let id: String = sp_map_get(vmap, "__PROSODY__")
if str_eq(id, "") {
return out
}
let out = native_list_append(out, parse_uint_from(id, "f0_median="))
let out = native_list_append(out, parse_uint_from(id, "f0_min="))
let out = native_list_append(out, parse_uint_from(id, "f0_max="))
let out = native_list_append(out, parse_uint_from(id, "declination="))
return out
}
+85
View File
@@ -0,0 +1,85 @@
// voice-profile.el - The VOICE signature as a pluggable PROFILE.
//
// Exact mirror of surface-profile.el / language-profile.el: a voice is a
// [String] slot-map read via voice_get, the SAME mechanism the realizer uses
// for language and surface. Where an instrument signature (a few dozen numbers)
// is the timbre of a musical tone, a VOICE signature is the timbre of the vocal
// tract the instrument that renders LANGUAGE-meaning as SPEECH on the audio
// surface. Physics (source-filter), not a recorded corpus.
//
// The signature is a few numbers, all integer (EL float arithmetic is unusable):
// name - label
// f0 - base pitch, Hz (glottal source rate at utterance start)
// f0_end - pitch at utterance end (declination -> falling = declarative)
// kf - formant scale in PER-MILLE (1000 = x1.0). Encodes vocal-tract
// length: shorter tract (child/female) -> higher kf. Scales every
// phoneme's nominal formant: F_actual = F_nominal * kf / 1000.
// dur - speaking-rate multiplier in per-mille (1000 = nominal; >1000 slower)
// tilt - source spectral tilt (per-mille; higher = darker/steeper rolloff)
// breath - breathiness 0..100 (aspiration mixed into the source)
//
// A voice is grabbed BY EAR (voice_analyze in speech.el extracts these numbers
// from a short PCM sample an impression, not 10h of training), or declared.
fn voice_new(name: String, f0: Int, f0_end: Int, kf: Int, dur: Int, tilt: Int, breath: Int) -> [String] {
let r: [String] = native_list_empty()
let r = native_list_append(r, "name")
let r = native_list_append(r, name)
let r = native_list_append(r, "f0")
let r = native_list_append(r, int_to_str(f0))
let r = native_list_append(r, "f0_end")
let r = native_list_append(r, int_to_str(f0_end))
let r = native_list_append(r, "kf")
let r = native_list_append(r, int_to_str(kf))
let r = native_list_append(r, "dur")
let r = native_list_append(r, int_to_str(dur))
let r = native_list_append(r, "tilt")
let r = native_list_append(r, int_to_str(tilt))
let r = native_list_append(r, "breath")
let r = native_list_append(r, int_to_str(breath))
return r
}
// Accessor identical convention to surface_get / lang_get.
fn voice_get(profile: [String], key: String) -> String {
let n: Int = native_list_len(profile)
let i: Int = 0
while i < n - 1 {
let k: String = native_list_get(profile, i)
if str_eq(k, key) {
return native_list_get(profile, i + 1)
}
let i = i + 2
}
return ""
}
fn voice_get_int(profile: [String], key: String) -> Int {
let s: String = voice_get(profile, key)
if str_eq(s, "") {
return 0
}
return str_to_int(s)
}
// -- Built-in voices ---------------------------------------------------------
// Neuron's own voice: calm, precise, androgynous-neutral. Low-ish base pitch,
// gentle declination, near-neutral vocal-tract length.
fn voice_neuron() -> [String] {
return voice_new("neuron", 112, 96, 1020, 1000, 1000, 6)
}
// Will's voice signature, built from the INGESTED geometry (f0/f0_end/kf read
// back from the will-voice manifold passed in, never hardcoded). Composable
// with an accent transform exactly like voice_neuron() (voice (+) accent).
fn voice_will(f0: Int, f0_end: Int, kf: Int) -> [String] {
return voice_new("will", f0, f0_end, kf, 1000, 1000, 6)
}
// A deliberately DISTINCT target voice for the imitation proof: higher pitch,
// shorter vocal tract (kf=1.20) -> a clearly different speaker. Neuron will
// HEAR a sample of this voice and reconstruct these numbers by ear.
fn voice_target_a() -> [String] {
return voice_new("target_a", 178, 150, 1200, 950, 1000, 10)
}
+45
View File
@@ -0,0 +1,45 @@
// speech-accent-demo.el - PROOF: Neuron speaks with a BRITISH accent, where the
// accent is a TRANSFORM composed onto the voice (voice (+) accent, separable),
// INGESTED as geometry (not a table). Same voice, accent toggled on/off = RP/GA.
fn main() {
let outdir: String = "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-acc02900ef4ade35e/elp/tests/examples/out/"
// LEARN: base phonetics + lexicon + the British-RP accent transform, all as
// ingested geometry (source -> manifold -> engram).
let pmap: [String] = ingest_phonetics("elp/data/phonetics.psv")
let lmap: [String] = ingest_lexicon("elp/data/lexicon.psv")
let amap: [String] = ingest_accent("elp/data/british-accent.psv")
println("[learn] phonemes=" + int_to_str(native_list_len(pmap) / 2) + " words=" + int_to_str(native_list_len(lmap) / 2) + " accent_targets=" + int_to_str(native_list_len(amap) / 2))
let neuron: [String] = voice_neuron()
let noaccent: [String] = native_list_empty()
// -- Sentence 1: "I am Neuron." from meaning ----------------------------
let fr1: [String] = sem_frame("describe", "I", "Neuron", "")
let t1: String = sem_realize(fr1)
let c1: [String] = text_phonemes(lmap, t1)
println("[s1] " + t1 + " :: " + list_join(c1, " "))
// separability: SAME voice, accent OFF (GA) vs ON (RP)
let ga: [Int] = synth_codes_accent(c1, neuron, pmap, noaccent)
let okga: Bool = write_wav(ga, 16000, outdir + "ga-neuron.wav")
let br1: [Int] = synth_codes_accent(c1, neuron, pmap, amap)
let okb1: Bool = write_wav(br1, 16000, outdir + "british-neuron.wav")
// -- Sentence 2: showcases NON-RHOTICITY --------------------------------
let fr2: [String] = sem_frame("describe", "I", "here", "")
let t2: String = sem_realize(fr2)
let c2: [String] = text_phonemes(lmap, t2)
let c2rp: [String] = apply_rhoticity(c2, pmap)
println("[s2] " + t2 + " :: GA=" + list_join(c2, " ") + " RP=" + list_join(c2rp, " "))
let br2: [Int] = synth_codes_accent(c2, neuron, pmap, amap)
let okb2: Bool = write_wav(br2, 16000, outdir + "british-2.wav")
// show an RP override read straight from the accent geometry
let ovAA: [Int] = accent_formants(amap, "AA")
if native_list_len(ovAA) >= 3 {
println("[accent-geometry] AA(LOT) RP f1=" + int_to_str(native_list_get(ovAA, 0)) + " f2=" + int_to_str(native_list_get(ovAA, 1)) + " (base GA 730/1090) [PROVISIONAL]")
}
println("[done] ga-neuron=" + bool_to_str(okga) + " british-neuron=" + bool_to_str(okb1) + " british-2=" + bool_to_str(okb2))
}
+69
View File
@@ -0,0 +1,69 @@
// speech-demo.el - PROOF: Neuron speaks from MEANING, rendered through INGESTED
// phonetic geometry, own-core, plus voice-by-IMITATION. Built by concatenating
// the elp realizer + voice-profile + speech-ingest + speech, then this main.
//
// LEARN : ingest acoustic-phonetics + lexicon SOURCES -> phoneme manifold in
// the engram (source -> manifold -> merge).
// MEANING : sem_frame("describe","I","Neuron","") -> sem_realize -> "I am Neuron."
// PHONES : words -> phoneme codes, READ from the ingested lexicon geometry.
// RENDER : superpose formant resonances (read from engram) over a glottal
// source -> own-core PCM/WAV, in Neuron's own voice.
// IMITATE : HEAR a short sample of a different voice -> extract its signature
// by ear (autocorrelation pitch + integer-DFT formant) -> render new
// speech in that voice. An impression, not a corpus.
fn speak_report(tag: String, codes: [String], voice: [String], pmap: [String], path: String) -> [Int] {
let s: [Int] = synth_codes(codes, voice, pmap)
let ok: Bool = write_wav(s, 16000, path)
println(tag + " samples=" + int_to_str(native_list_len(s)) + " ok=" + bool_to_str(ok) + " -> " + path)
return s
}
fn main() {
let outdir: String = "/private/tmp/claude-501/-Users-will/6531446d-bc27-4095-930b-e04777c3db4f/scratchpad/"
// -- LEARN: ingest the speech primitives as geometry --------------------
let pmap: [String] = ingest_phonetics("elp/data/phonetics.psv")
let lmap: [String] = ingest_lexicon("elp/data/lexicon.psv")
let saved: Bool = engram_save(outdir + "phoneme-manifold.json")
println("[learn] phonemes=" + int_to_str(native_list_len(pmap) / 2) + " words=" + int_to_str(native_list_len(lmap) / 2) + " manifold_saved=" + bool_to_str(saved))
// sanity: show that AA's formants came from ingested geometry, not code
let aa: [Int] = phon_geo(pmap, "AA")
let aaF1: Int = native_list_get(aa, 0)
let aaF2: Int = native_list_get(aa, 1)
println("[read-geometry] AA F1=" + int_to_str(aaF1) + " F2=" + int_to_str(aaF2) + " (parsed from engram node)")
// -- MEANING -> WORDS via the realizer's language faculty ----------------
let frame: [String] = sem_frame("describe", "I", "Neuron", "")
let text: String = sem_realize(frame)
println("[meaning->text] " + text)
// -- WORDS -> PHONEMES (read from ingested lexicon geometry) --------------
let codes: [String] = text_phonemes(lmap, text)
println("[phonemes] " + list_join(codes, " "))
// -- RENDER in Neuron's own voice ----------------------------------------
let neuron: [String] = voice_neuron()
let s1: [Int] = speak_report("[speak neuron]", codes, neuron, pmap, outdir + "neuron.wav")
// -- IMITATION: hear a distinct voice, recover its signature, re-render ---
let vA: [String] = voice_target_a()
let hcodes: [String] = native_list_empty()
hcodes = native_list_append(hcodes, "SIL")
let z: Int = 0
while z < 6 {
hcodes = native_list_append(hcodes, "AA")
z = z + 1
}
hcodes = native_list_append(hcodes, "SIL")
let heard: [Int] = synth_codes(hcodes, vA, pmap)
let okh: Bool = write_wav(heard, 16000, outdir + "heard.wav")
let vB: [String] = voice_analyze(heard, 16000)
println("[imitate] heard ACTUAL f0=" + voice_get(vA, "f0") + " kf=" + voice_get(vA, "kf"))
println("[imitate] heard RECOVERED f0=" + voice_get(vB, "f0") + " kf=" + voice_get(vB, "kf") + " (extracted by ear from PCM)")
let s2: [Int] = speak_report("[speak imitation]", codes, vB, pmap, outdir + "imitation.wav")
println("[done] rendered from meaning + ingested geometry; imitation from a heard sample.")
}
+48
View File
@@ -0,0 +1,48 @@
// speech-organ-demo.el - PROOF: the render now reads its phoneme + accent
// GEOMETRY from the ingest ORGAN's saved engram files (engram_load +
// engram_scan_nodes_json + cache), not a same-run hand-load. The British accent
// is still a composed transform-geometry (voice (+) accent, separable). Numbers
// come from the organ manifold; the .psv supplies only categorical vowel-class.
fn main() {
let outdir: String = "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-acc02900ef4ade35e/elp/tests/examples/out/"
// engram-independent caches from source (survive engram_load replacement)
let vset: [String] = organ_vset("elp/data/phonetics.psv")
let lmap: [String] = organ_lex("elp/data/lexicon.psv")
// ORGAN read: phonetics FIRST (cache), THEN accent (engram_load replaces store)
let pmap: [String] = organ_pmap("elp/data/phonetics-formants.engram.json")
let amap: [String] = organ_amap("elp/data/british-accent.engram.json")
println("[organ] phon_syms=" + int_to_str(native_list_len(pmap) / 2) + " accent_syms=" + int_to_str(native_list_len(amap) / 2) + " vowels=" + int_to_str(native_list_len(vset)) + " words=" + int_to_str(native_list_len(lmap) / 2))
// prove the numbers came from the organ node content
let g: [Int] = phon_geo(pmap, "AA")
println("[organ-read] phoneme AA f1=" + int_to_str(native_list_get(g, 0)) + " f2=" + int_to_str(native_list_get(g, 1)) + " f3=" + int_to_str(native_list_get(g, 2)) + " (P&B1952 MEASURED)")
let ov: [Int] = accent_formants(amap, "AA")
if native_list_len(ov) >= 3 {
println("[organ-read] accent AA(LOT) f1=" + int_to_str(native_list_get(ov, 0)) + " f2=" + int_to_str(native_list_get(ov, 1)) + " (DERIVED RP, PROVISIONAL)")
}
println("[organ-read] non_rhotic=" + int_to_str(is_nonrhotic(amap)))
let neuron: [String] = voice_neuron()
let noacc: [String] = native_list_empty()
// Sentence 1: "I am Neuron." from meaning; GA vs RP = separable toggle
let t1: String = sem_realize(sem_frame("describe", "I", "Neuron", ""))
let c1: [String] = text_phonemes(lmap, t1)
println("[s1] " + t1 + " :: " + list_join(c1, " "))
let ga: [Int] = synth_codes_accent(c1, neuron, pmap, noacc, vset)
let okga: Bool = write_wav(ga, 16000, outdir + "ga-neuron-organ.wav")
let br1: [Int] = synth_codes_accent(c1, neuron, pmap, amap, vset)
let okb1: Bool = write_wav(br1, 16000, outdir + "british-neuron-organ.wav")
// Sentence 2: non-rhoticity showcase
let t2: String = sem_realize(sem_frame("describe", "I", "here", ""))
let c2: [String] = text_phonemes(lmap, t2)
let c2rp: [String] = apply_rhoticity(c2, vset)
println("[s2] " + t2 + " :: GA=" + list_join(c2, " ") + " RP=" + list_join(c2rp, " "))
let br2: [Int] = synth_codes_accent(c2, neuron, pmap, amap, vset)
let okb2: Bool = write_wav(br2, 16000, outdir + "british-2-organ.wav")
println("[done] ga-organ=" + bool_to_str(okga) + " british-organ=" + bool_to_str(okb1) + " british-2-organ=" + bool_to_str(okb2))
}
+31
View File
@@ -0,0 +1,31 @@
// speech-voice-demo.el - LIVE VOICE LOOP (stand-in test). Capture -> voiceprint
// -> reshape -> INGEST AS GEOMETRY -> read the target back FROM geometry -> the
// EL projector renders a line reaching for that voice. Stand-in "Will" = the
// voiceprint of imitation.wav. HONEST: pitch + coarse vocal-tract scale, NOT a clone.
fn main() {
let outdir: String = "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-acc02900ef4ade35e/elp/tests/examples/out/"
let vp: String = "/private/tmp/claude-501/-Users-will/6531446d-bc27-4095-930b-e04777c3db4f/scratchpad/will-voiceprint.json"
// 1+2: reshape voiceprint JSON -> organ voice-signature source
let sig0: [String] = native_list_empty()
let sig: [Int] = reshape_voiceprint(vp, "elp/data/will-voice.json")
// 3: ingest as geometry + engram_save a reloadable manifold file
let ig: Int = ingest_voice(sig, "elp/data/will-voice.engram.json")
// 4: READ the target back FROM geometry (engram_load + scan + filter)
let g: [Int] = load_voice("elp/data/will-voice.engram.json")
println("[voice-geometry] read from manifold: f0=" + int_to_str(native_list_get(g, 0)) + " f0_end=" + int_to_str(native_list_get(g, 1)) + " kf=" + int_to_str(native_list_get(g, 2)) + " f1=" + int_to_str(native_list_get(g, 3)) + " f2=" + int_to_str(native_list_get(g, 4)) + " f3=" + int_to_str(native_list_get(g, 5)) + " (measured, COARSE — not a clone)")
// phoneme geometry from the organ (loaded AFTER the voice sig is cached in EL)
let pmap: [String] = organ_pmap("elp/data/phonetics-formants.engram.json")
let lmap: [String] = organ_lex("elp/data/lexicon.psv")
// 5: render a line FROM MEANING in Will's voice
let vw: [String] = voice_will(native_list_get(g, 0), native_list_get(g, 1), native_list_get(g, 2))
let t: String = sem_realize(sem_frame("greet", "Will", "", ""))
let codes: [String] = text_phonemes(lmap, t)
println("[render] \"" + t + "\" :: " + list_join(codes, " ") + " in voice=will f0=" + int_to_str(voice_get_int(vw, "f0")) + " kf=" + int_to_str(voice_get_int(vw, "kf")))
let samples: [Int] = synth_codes(codes, vw, pmap)
let ok: Bool = write_wav(samples, 16000, outdir + "will-reply.wav")
println("[done] will-reply.wav=" + bool_to_str(ok))
}
+48
View File
@@ -0,0 +1,48 @@
// speech-voice-demo2.el - LIVE VOICE LOOP on Will's richer 30s read, with a
// GEOMETRIC SET-REPLACE of the voice_will manifold (supersede the coarse 10s
// region, insert the 30s region no duplicate node, no per-node CRUD; Will's
// standing rule f999c5ff). HONEST: 30s steadies the 11-number average over more
// of his vowels, but it is still one formant triple with no coarticulation or
// prosody closer but still synthetic, not a clone.
fn main() {
let outdir: String = "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-acc02900ef4ade35e/elp/tests/examples/out/"
let vp: String = "/private/tmp/claude-501/-Users-will/6531446d-bc27-4095-930b-e04777c3db4f/scratchpad/will30-voiceprint.json"
let manifest: String = "elp/data/will-voice.engram.json"
// --- SET-REPLACE step 1: read the PRIOR region (text read of the manifold
// file no engram_load, so the store stays clean) and report what is
// being superseded. ---
let prior: String = fs_read(manifest)
let pp: Int = str_index_of(prior, "voice will ")
if pp >= 0 {
let pw: String = str_slice(prior, pp, pp + 200)
println("[set-replace] superseding PRIOR voice region: f0=" + int_to_str(parse_uint_from(pw, "f0=")) + " kf=" + int_to_str(parse_uint_from(pw, "kf=")) + " f1=" + int_to_str(parse_uint_from(pw, "f1=")))
}
// --- step 2: reshape the 30s voiceprint -> organ voice-signature source ---
let sig: [Int] = reshape_voiceprint(vp, "elp/data/will-voice.json")
// --- step 3: INSERT the fresh 30s region into an EMPTY engram and save ->
// wholesale replaces the manifold file (old region dropped, not edited,
// not duplicated). This is the geometric set-replace. ---
let ig: Int = ingest_voice(sig, manifest)
// --- step 4: READ the new target BACK from geometry ---
let g: [Int] = load_voice(manifest)
println("[voice-geometry] new region read from manifold: f0=" + int_to_str(native_list_get(g, 0)) + " f0_end=" + int_to_str(native_list_get(g, 1)) + " kf=" + int_to_str(native_list_get(g, 2)) + " f1=" + int_to_str(native_list_get(g, 3)) + " f2=" + int_to_str(native_list_get(g, 4)) + " f3=" + int_to_str(native_list_get(g, 5)) + " (measured 30s, COARSE — not a clone)")
// phoneme + lexicon geometry from the organ (loaded after the voice sig is
// cached in EL, since engram_load replaces the store)
let pmap: [String] = organ_pmap("elp/data/phonetics-formants.engram.json")
let lmap: [String] = organ_lex("elp/data/lexicon.psv")
// --- step 5: render a fresh reply FROM MEANING in the 30s Will voice ---
let vw: [String] = voice_will(native_list_get(g, 0), native_list_get(g, 1), native_list_get(g, 2))
let t: String = sem_realize(sem_frame("greet", "Will", "", ""))
let codes: [String] = text_phonemes(lmap, t)
println("[render] \"" + t + "\" :: " + list_join(codes, " ") + " in voice=will f0=" + int_to_str(voice_get_int(vw, "f0")) + " kf=" + int_to_str(voice_get_int(vw, "kf")))
let samples: [Int] = synth_codes(codes, vw, pmap)
let ok: Bool = write_wav(samples, 16000, outdir + "will-reply2.wav")
println("[done] will-reply2.wav=" + bool_to_str(ok))
}
@@ -0,0 +1,37 @@
// speech-voicegeom-demo.el - THE JUMP: render Will's VOWEL SPACE + PROSODY
// (measured over 30s), not the single 11-number average. His vowels land at HIS
// targets; pitch follows HIS melody. All read back FROM the ingested geometry.
// INTERIM: the geometry was Python-measured (measure_voice.py, numpy LPC/F0)
// to be superseded by the engram-measures-audio path. No source layer.
fn main() {
let outdir: String = "/Users/will/Development/neuron-technologies/foundation/el/.claude/worktrees/agent-acc02900ef4ade35e/elp/tests/examples/out/"
// 1: ingest vowel space + prosody as geometry (empty store -> save; set-replace)
let ig: Int = ingest_voicegeom("elp/data/will-vowelspace.psv", "elp/data/will-prosody.psv", "elp/data/will-voicegeom.engram.json")
// kf (vocal-tract scale for consonants) from the earlier will-voice manifold
let sigv: [Int] = load_voice("elp/data/will-voice.engram.json")
let kf: Int = native_list_get(sigv, 2)
// 2: read vowel space + prosody back FROM geometry
let vmap: [String] = load_voicegeom("elp/data/will-voicegeom.engram.json")
let pros: [Int] = prosody_from(vmap)
println("[geometry] vowels=" + int_to_str((native_list_len(vmap) - 2) / 2) + " prosody f0_median=" + int_to_str(native_list_get(pros, 0)) + " f0_min=" + int_to_str(native_list_get(pros, 1)) + " f0_max=" + int_to_str(native_list_get(pros, 2)) + " kf=" + int_to_str(kf))
let ehv: [Int] = vmap_get(vmap, "EH")
let ihv: [Int] = vmap_get(vmap, "IH")
println("[his-vowels] EH=" + int_to_str(native_list_get(ehv, 0)) + "/" + int_to_str(native_list_get(ehv, 1)) + " IH=" + int_to_str(native_list_get(ihv, 0)) + "/" + int_to_str(native_list_get(ihv, 1)))
// phoneme geometry from the organ (loaded AFTER caches are in EL)
let pmap: [String] = organ_pmap("elp/data/phonetics-formants.engram.json")
let lmap: [String] = organ_lex("elp/data/lexicon.psv")
// 3+4: render FROM MEANING in his-vowels + his-prosody voice
let vw: [String] = voice_will(native_list_get(pros, 0), native_list_get(pros, 1), kf)
let noacc: [String] = native_list_empty()
let novset: [String] = native_list_empty()
let t: String = sem_realize(sem_frame("greet", "Will", "", ""))
let codes: [String] = text_phonemes(lmap, t)
println("[render] \"" + t + "\" :: " + list_join(codes, " "))
let samples: [Int] = synth_codes_accent(codes, vw, pmap, noacc, novset, vmap, pros)
let ok: Bool = write_wav(samples, 16000, outdir + "will-reply3.wav")
println("[done] will-reply3.wav=" + bool_to_str(ok))
}
-120
View File
@@ -1,120 +0,0 @@
# sandbox — the Neuron STACK sandbox
**Work on a whole stack at once, not one repo at a time.** `sandbox` assembles every
constituent repo of a named stack into **one combined worktree workspace**, wired so
they build and run **together**, on an isolated clean base — then tears it all down
cleanly. The live soul/engram (`:7770` / `:8742`) are never touched.
It is the multi-repo sibling of [`nsbx`](./README.md): where `nsbx dev` stands up
**one** repo's worktree + an isolated engram, `sandbox` stands up **every** repo of a
stack as sibling git worktrees under a single workspace.
```bash
export PATH="$PWD:$PATH" # or symlink `sandbox` onto your PATH
sandbox neuron-stack tim # el + neuron soul + NeuronUI, assembled together
cd ~/Development/neuron-technologies/stack-worktrees/neuron-stack-tim
source .stack-env # EL_REPO + PATH now point at the SANDBOX el
./build.sh # engram compiles, soul compiles, UI present & buildable
sandbox down neuron-stack tim # remove every worktree; live untouched
```
## The two profiles
### `el-stack` — the whole EL kit
The compiler + language + framework + tooling are **all one repo** (`foundation/el`:
`lang/` = elc/elb + runtime, `engram/src/server.el`, `elp/` = NLG, `ui/` = the **el-ui
framework**, plus `ql`, `ide`, `epm`, `arbor`, `tools`). Its downstream SDK consumers
come along so a change to `elc` can be proven end-to-end across the kit.
| repo | required | role |
|------|----------|------|
| `foundation/el` | ✓ | elc + elb compiler, el_runtime, engram source, **el-ui framework**, elp/ql/ide/epm tooling |
| `engram-language` | | language-faculty reference POC (Python) — being ported into `el/elp` |
| `foundation/forge` | | downstream SDK consumer — `make build` |
| `foundation/dharma` | | downstream SDK consumer — CGI provenance registry |
`build.sh` proves it: `elc` compiles a real stack source and `cc` links it against the
runtime into a native binary (elc + runtime build together), and — if present — `forge`
builds against the freshly-assembled SDK.
### `neuron-stack` — the full product
Substrate + soul + UI. **Engram is not a separate repo** — its source lives inside
`foundation/el`.
| repo | required | role |
|------|----------|------|
| `foundation/el` | ✓ | substrate: elc + el_runtime + engram source + the `elp` NLG the soul imports |
| `neuron` | ✓ | the soul (`:7770`) + engram build; `soul.el` imports `../foundation/el/elp/src/elp.el` |
| `products/NeuronUI` | ✓ | the app/UI (Kotlin/Compose desktop client; bundles the soul binary) |
| `products/web` | | marketing site + interactive soul-demo |
`build.sh` proves it: **engram** builds (`elc engram/src/server.el``cc … el_runtime.c`
→ native binary), the **soul** compiles with its cross-repo `../foundation/el` import
resolving to the *sandbox* el, and the **UI** is present with its build entry.
## Why it works — mirrored-layout wiring
The repos reference each other by **relative sibling paths** (e.g. the soul imports
`../foundation/el/elp/src/elp.el`). So `sandbox` lays every worktree out at its **natural
relative path** inside the workspace:
```
stack-worktrees/neuron-stack-tim/
├── foundation/el/ ← worktree of foundation/el (the sandbox el)
├── neuron/ ← worktree of neuron
└── products/NeuronUI/ ← worktree of products/NeuronUI
```
From `neuron/`, `../foundation/el` resolves to `…/neuron-stack-tim/foundation/el` — the
**sandbox** copy, never the live tree. No symlinks, no path rewriting: the layout *is*
the wiring. `.stack-env` additionally pins `EL_REPO` and prepends the sandbox `elc`/`elb`
to `PATH`.
## Commands
| command | does |
|---------|------|
| `sandbox el-stack <name> [--minimal]` | assemble the EL kit (`--minimal` = required repos only) |
| `sandbox neuron-stack <name> [--minimal]` | assemble the full product |
| `sandbox build <profile> <name>` | run the workspace's combined `build.sh` |
| `sandbox status <profile> <name>` | per-repo head + clean/dirty |
| `sandbox list` | list assembled workspaces |
| `sandbox down <profile> <name> [--delete-branch]` | remove every worktree + drop the workspace (branch kept unless `--delete-branch`) |
Flags: `--minimal` (required repos only), `--branch B` (branch name; default
`sandbox/<profile>-<name>`), `--base REF` (fork point; default each repo's committed
HEAD).
## Rails (always)
- **Clean base** — worktrees fork off each repo's **committed HEAD**; the dirty state of
the live checkout is deliberately *not* carried in.
- **Persistent** — the workspace lives under `NSBX_STACK_ROOT` (default
`~/Development/neuron-technologies/stack-worktrees`), **never `/tmp`** (ablated on
compaction).
- **Never touches live** — `sandbox` only does `git worktree` + offline `cc`. It never
binds `:8742`/`:7770`, never `launchctl`, never `pkill`. Bringing up an **isolated
engram** is delegated, opt-in, to `nsbx` (which guards the live store and refuses the
live ports).
- **Idempotent & safe** — refuses to clobber an existing workspace; a failed assembly
rolls back its partial worktrees; teardown removes worktrees through their origin repo
and prunes.
- **Own-the-core** — pure bash + `git worktree`. No new dependencies.
## Env knobs
`NSBX_STACK_ROOT` (workspace root), `NEURON_DEV_ROOT` (the dir holding all the peer
repos, default `~/Development/neuron-technologies`).
## Isolated engram for `neuron-stack` (opt-in)
`sandbox` gets the code building together; to run the soul against an **isolated** engram
(never live), delegate to `nsbx` from inside the workspace:
```bash
source .stack-env
nsbx create $STACK_NAME --source "$EL_REPO" # clone live store onto a non-default port
nsbx up $STACK_NAME
nsbx status $STACK_NAME # prints the isolated engram URL
```
-505
View File
@@ -1,505 +0,0 @@
#!/usr/bin/env bash
# sandbox — the Neuron STACK sandbox: assemble a WHOLE stack of repos into ONE
# combined worktree workspace, wired so they build/run TOGETHER, on an isolated
# clean base — so an agent (or Will) can work on the full stack at once instead of
# one repo at a time.
#
# It is the multi-repo generalisation of `nsbx` (this same directory): where
# `nsbx dev` stands up ONE repo's worktree + an isolated engram, `sandbox` stands
# up EVERY constituent repo of a named stack as sibling git worktrees under a
# single workspace, mirroring their on-disk relative layout so the cross-repo
# `../foundation/el` imports resolve to the SANDBOX copy — never the live tree.
#
# sandbox el-stack <name> # elc compiler + EL language + framework + tooling (+ consumers)
# sandbox neuron-stack <name> # runtime/soul + engram + app/UI (the full product)
# sandbox list # list assembled stack workspaces
# sandbox status <profile> <name> # inspect one
# sandbox build <profile> <name> # run the workspace's combined build.sh
# sandbox down <profile> <name> # tear down: remove every worktree, drop the workspace
#
# RAILS (always):
# * worktrees fork off each repo's COMMITTED HEAD -> a clean, reproducible base
# (the dirty state of the live checkout is deliberately NOT carried in).
# * the workspace lives at a PERSISTENT path (never /tmp — ablated on compaction).
# * NEVER touches the live soul/engram (:7770 / :8742). It only creates git
# worktrees + a build script; bringing up an isolated engram is delegated,
# opt-in, to `nsbx` (which already guards the live store & ports).
# * idempotent & safe: refuses to clobber an existing workspace; teardown removes
# worktrees through their origin repo and prunes — branches are kept by default.
# * own-the-core: pure bash + git worktree. No new dependencies.
set -uo pipefail
# ---------------------------------------------------------------- constants ----
# Root that holds all the peer repos (neuron, foundation/el, products/*, ...).
DEV_ROOT="${NEURON_DEV_ROOT:-$HOME/Development/neuron-technologies}"
# Where assembled stack workspaces live (persistent; sibling to el-worktrees/).
STACK_ROOT="${NSBX_STACK_ROOT:-$DEV_ROOT/stack-worktrees}"
EL_REPO_REL="foundation/el"
LIVE_ENGRAM_PORT=8742 # live engram — sandbox must never bind it
LIVE_SOUL_PORT=7770 # live soul — sandbox must never bind it
# nsbx (single-repo isolated-engram tool) lives next to this script.
NSBX="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)/nsbx"
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'; C_CYN=$'\033[36m'; C_DIM=$'\033[2m'; C_BLD=$'\033[1m'; C_0=$'\033[0m'
# ---------------------------------------------------------------- helpers ------
die(){ printf '%serror:%s %s\n' "$C_RED" "$C_0" "$*" >&2; exit 1; }
log(){ printf '%s==>%s %s\n' "$C_BLD" "$C_0" "$*" >&2; }
info(){ printf ' %s\n' "$*" >&2; }
ok(){ printf ' %s%s%s\n' "$C_GRN" "$*" "$C_0" >&2; }
warn(){ printf ' %s%s%s\n' "$C_YEL" "$*" "$C_0" >&2; }
need(){ command -v "$1" >/dev/null 2>&1 || die "missing dependency: $1"; }
# ---------------------------------------------------------------- profiles -----
# profile_repos <profile> : emit one line per constituent repo:
# <relpath-under-DEV_ROOT> | <required|optional> | <role>
# The relpath is preserved INSIDE the workspace, so all cross-repo `../foundation/el`
# references resolve to the sandbox copy automatically (mirrored-layout wiring).
profile_repos(){
case "$1" in
el-stack)
# The compiler+language+framework+tooling are all ONE repo (foundation/el).
# Its downstream SDK consumers (forge, dharma) + the language-faculty POC come
# along so a change to elc can be proven end-to-end across the kit.
cat <<'EOF'
foundation/el | required | elc + elb compiler, el_runtime, engram source, el-ui framework, elp/ql/ide/epm tooling
engram-language | optional | language-faculty reference POC (Python) — ported into el/elp
foundation/forge | optional | downstream SDK consumer — `make build` (imprint forge CLI)
foundation/dharma | optional | downstream SDK consumer — CGI provenance registry
EOF
;;
neuron-stack)
# The full product: substrate (el) + soul + UI. Engram is NOT a separate repo
# (its source lives in foundation/el/engram/src/server.el).
cat <<'EOF'
foundation/el | required | substrate: elc + el_runtime + engram source + elp NLG the soul imports
neuron | required | the soul (:7770) + engram build; soul.el imports ../foundation/el/elp/src/elp.el
products/NeuronUI | required | the app/UI (Kotlin/Compose desktop client; bundles the soul binary)
products/web | optional | marketing site + interactive soul-demo
EOF
;;
*) return 1;;
esac
}
is_profile(){ profile_repos "$1" >/dev/null 2>&1; }
ws_dir(){ printf '%s/%s-%s' "$STACK_ROOT" "$1" "$2"; } # <root>/<profile>-<name>
ws_branch(){ printf 'sandbox/%s-%s' "$1" "$2"; } # branch name used in each repo
manifest(){ printf '%s/.stack-manifest.json' "$1"; } # <ws>/.stack-manifest.json
# ================================================================ up ===========
cmd_up(){
local profile="$1"; shift
local name="" branch="" base_override="" minimal=0
[ $# -gt 0 ] && [ "${1#-}" = "$1" ] && { name="$1"; shift; } || die "usage: sandbox $profile <name> [--minimal] [--branch B] [--base REF]"
while [ $# -gt 0 ]; do case "$1" in
--minimal) minimal=1; shift;;
--branch) branch="$2"; shift 2;;
--base) base_override="$2"; shift 2;;
*) die "unknown flag: $1";;
esac; done
need git
is_profile "$profile" || die "unknown profile: $profile (try: el-stack | neuron-stack)"
local ws; ws="$(ws_dir "$profile" "$name")"
[ -n "$branch" ] || branch="$(ws_branch "$profile" "$name")"
# -------- pre-flight (fail before creating anything) --------
case "$ws" in /tmp/*|/private/tmp/*|/var/tmp/*)
die "refusing workspace under a temp dir ($ws) — temp dirs are ablated on compaction; set NSBX_STACK_ROOT to a persistent path";;
esac
[ -e "$ws" ] && die "workspace already exists: $ws (sandbox down $profile $name first)"
# resolve + validate every repo, and pick a base sha per repo, BEFORE touching disk
local -a rels roles bases origins wts
local line rel role_extra role req origin base wt
while IFS= read -r line; do
[ -z "${line// }" ] && continue
rel="$(printf '%s' "$line" | cut -d'|' -f1 | xargs)"
req="$(printf '%s' "$line" | cut -d'|' -f2 | xargs)"
role="$(printf '%s' "$line" | cut -d'|' -f3- | sed 's/^ *//')"
[ "$minimal" -eq 1 ] && [ "$req" = "optional" ] && continue
origin="$DEV_ROOT/$rel"
git -C "$origin" rev-parse --git-dir >/dev/null 2>&1 || {
[ "$req" = "required" ] && die "required repo missing or not a git repo: $origin"
warn "skipping optional repo (missing): $rel"; continue; }
if [ -n "$base_override" ]; then base="$base_override"; else base="$(git -C "$origin" rev-parse HEAD)"; fi
wt="$ws/$rel"
[ -e "$wt" ] && die "target worktree path already exists: $wt"
rels+=("$rel"); roles+=("$role"); origins+=("$origin"); bases+=("$base"); wts+=("$wt")
done < <(profile_repos "$profile")
[ "${#rels[@]}" -gt 0 ] || die "no repos resolved for profile $profile"
log "assembling '$profile' workspace '$name'"
info "workspace: $ws"
info "branch: $branch (created in each repo, off its committed HEAD)"
mkdir -p "$ws"
# -------- create a worktree per repo (mirrored relpath layout) --------
local i n="${#rels[@]}"
SB_DONE_WTS=(); SB_DONE_ORIGINS=()
for ((i=0; i<n; i++)); do
rel="${rels[$i]}"; origin="${origins[$i]}"; base="${bases[$i]}"; wt="${wts[$i]}"
mkdir -p "$(dirname "$wt")"
local gerr
if git -C "$origin" show-ref --verify --quiet "refs/heads/$branch"; then
gerr="$(git -C "$origin" worktree add "$wt" "$branch" 2>&1)" \
|| { _rollback; die "git worktree add failed for $rel (existing branch $branch):"$'\n'" $gerr"; }
else
gerr="$(git -C "$origin" worktree add -b "$branch" "$wt" "$base" 2>&1)" \
|| { _rollback; die "git worktree add -b $branch failed for $rel (base $base):"$'\n'" $gerr"; }
fi
SB_DONE_WTS+=("$wt"); SB_DONE_ORIGINS+=("$origin")
ok "worktree: $rel -> ${wt#$ws/} (branch $branch @ ${base:0:9})"
done
local el_ws="$ws/$EL_REPO_REL"
_write_env "$ws" "$profile" "$name" "$branch" "$el_ws"
_write_manifest "$ws" "$profile" "$name" "$branch"
_write_build "$ws" "$profile" "$el_ws"
_write_readme "$ws" "$profile" "$name" "$branch" "$el_ws"
# -------- summary --------
echo >&2
printf '%s STACK WORKSPACE READY — %s / %s%s\n' "$C_BLD" "$profile" "$name" "$C_0" >&2
printf ' %-11s %s\n' "workspace" "$ws" >&2
printf ' %-11s %s\n' "branch" "$branch (in each repo)" >&2
printf ' %-11s %s\n' "repos" "$n worktrees, mirrored layout" >&2
echo >&2
info "get in: cd $ws && source .stack-env"
info "build all: sandbox build $profile $name # (or: cd $ws && ./build.sh)"
if [ "$profile" = "neuron-stack" ]; then
info "isolated engram (opt-in, via nsbx):"
info " nsbx create $profile-$name --source $el_ws && nsbx up $profile-$name"
fi
info "tear down: sandbox down $profile $name # removes all worktrees; branches kept"
}
# _rollback : remove any worktrees already created this run (globals set by cmd_up)
SB_DONE_WTS=(); SB_DONE_ORIGINS=()
_rollback(){
local j
[ "${#SB_DONE_WTS[@]}" -gt 0 ] && warn "rolling back ${#SB_DONE_WTS[@]} partial worktree(s)"
for ((j=${#SB_DONE_WTS[@]}-1; j>=0; j--)); do
git -C "${SB_DONE_ORIGINS[$j]}" worktree remove --force "${SB_DONE_WTS[$j]}" 2>/dev/null || rm -rf "${SB_DONE_WTS[$j]}"
git -C "${SB_DONE_ORIGINS[$j]}" worktree prune 2>/dev/null || true
done
}
# ---------------------------------------------------------------- writers ------
_write_env(){
local ws="$1" profile="$2" name="$3" branch="$4" el_ws="$5"
local elc_dir="$el_ws/lang/dist/platform"
cat > "$ws/.stack-env" <<ENV
# stack env for '$profile/$name' — SOURCE this to work the whole stack together.
# Pins EL_REPO + PATH at the SANDBOX copy of foundation/el, so elc/elb/runtime and
# every cross-repo ../foundation/el import resolve INSIDE this workspace.
# The live mind (:$LIVE_ENGRAM_PORT engram / :$LIVE_SOUL_PORT soul) is deliberately NOT referenced.
export STACK_NAME="$profile-$name"
export STACK_PROFILE="$profile"
export STACK_ROOT_WS="$ws"
export EL_REPO="$el_ws"
export PATH="$elc_dir:\$PATH" # elc, elb (darwin/linux prebuilt) from the sandbox el
ENV
if [ "$profile" = "neuron-stack" ]; then
cat >> "$ws/.stack-env" <<ENV
export NEURON_REPO="$ws/neuron"
export NEURONUI_REPO="$ws/products/NeuronUI"
# engram/soul are NOT bound here — bring up an ISOLATED engram via nsbx when needed
# (nsbx guards the live store & refuses ports :$LIVE_ENGRAM_PORT/:$LIVE_SOUL_PORT):
# nsbx create $profile-$name --source \$EL_REPO && nsbx up $profile-$name
# nsbx status $profile-$name # prints the isolated engram URL to point the soul at
ENV
fi
# direnv convenience
[ -e "$ws/.envrc" ] || printf 'source_env .stack-env 2>/dev/null || source .stack-env\n' > "$ws/.envrc"
}
_write_manifest(){
local ws="$1" profile="$2" name="$3" branch="$4"
# emit worktree records from git's own worktree list, filtered to this workspace
python3 - "$ws" "$profile" "$name" "$branch" "$DEV_ROOT" <<'PY'
import json, os, subprocess, sys
ws, profile, name, branch, dev = sys.argv[1:6]
repos = []
for rel in sorted(os.listdir(ws)) if False else []:
pass
# discover worktrees by walking one level of relpaths we created
def git(root, *a):
return subprocess.run(["git","-C",root,*a], capture_output=True, text=True).stdout.strip()
for dirpath, dirnames, filenames in os.walk(ws):
if ".git" in filenames or ".git" in dirnames:
rel = os.path.relpath(dirpath, ws)
toplevel = git(dirpath, "rev-parse", "--show-toplevel")
common = git(dirpath, "rev-parse", "--git-common-dir")
origin = os.path.realpath(os.path.join(common, ".."))
head = git(dirpath, "rev-parse", "HEAD")
repos.append({"rel": rel, "worktree": dirpath, "origin": origin,
"branch": branch, "head": head})
dirnames[:] = [] # don't descend into a repo
repos.sort(key=lambda r: r["rel"])
json.dump({"profile": profile, "name": name, "branch": branch,
"workspace": ws, "repos": repos},
open(os.path.join(ws, ".stack-manifest.json"), "w"), indent=2)
PY
}
_write_build(){
local ws="$1" profile="$2" el_ws="$3"
cat > "$ws/build.sh" <<'BUILD'
#!/usr/bin/env bash
# build.sh — build the assembled stack together, in dependency order.
# Generated by `sandbox`. Run from the workspace root (it sources .stack-env).
set -uo pipefail
cd "$(dirname "$0")"; source ./.stack-env
say(){ printf '\033[1m==>\033[0m %s\n' "$*"; }
ok(){ printf ' \033[32m%s\033[0m\n' "$*"; }
bad(){ printf ' \033[31m%s\033[0m\n' "$*"; }
# locate an elc that runs on THIS machine (darwin-arm64 / linux-amd64), from the sandbox el
find_elc(){
local d="$EL_REPO/lang/dist/platform"
case "$(uname -s)-$(uname -m)" in
Darwin-arm64) echo "$d/elc-darwin-arm64";;
Linux-x86_64) echo "$d/elc-linux-amd64";;
*) echo "$d/elc";;
esac
}
ELC="$(find_elc)"; [ -x "$ELC" ] || ELC="$EL_REPO/lang/dist/platform/elc"
say "elc: $ELC"
[ -x "$ELC" ] && ok "$("$ELC" 2>&1 | head -1 || echo present)" || { bad "elc not executable"; exit 1; }
# canonical runtime C to link (CI-published release copy; ~8 copies exist in-tree)
RT="$EL_REPO/lang/releases/v1.0.0-20260501"
[ -f "$RT/el_runtime.c" ] || RT="$EL_REPO/lang/el-compiler/runtime"
[ -f "$RT/el_runtime.c" ] && ok "el_runtime: $RT/el_runtime.c" || bad "no el_runtime.c found under $EL_REPO/lang"
BUILD
if [ "$profile" = "el-stack" ]; then
cat >> "$ws/build.sh" <<'BUILD'
# ---- EL STACK: prove elc + the el stuff (incl. the el-ui framework) build together ----
say "el-ui framework present: $EL_REPO/ui"
[ -d "$EL_REPO/ui" ] && ok "framework dir present ($(ls "$EL_REPO/ui" | tr '\n' ' '))" || bad "no ui/ dir"
# end-to-end compiler proof: elc compiles a real, substantial stack source to C,
# then cc links it against the runtime -> a working native binary.
B="$(mktemp -d)"
say "elc end-to-end: compile engram/src/server.el and link a native binary"
if "$ELC" "$EL_REPO/engram/src/server.el" > "$B/x.c" 2>"$B/elc.err"; then
ok "elc -> C ($(wc -c <"$B/x.c" | tr -d ' ') bytes)"
if cc -std=c11 -O2 -w -I "$RT" -o "$B/x" "$B/x.c" "$RT/el_runtime.c" -lcurl -lpthread -lm 2>"$B/cc.err"; then
ok "cc link ok -> native binary $(ls -lh "$B/x" | awk '{print $5}') (elc + runtime build together)"
else
bad "cc link failed:"; grep -i 'error:' "$B/cc.err" | sort -u | head | sed 's/^/ /'
fi
else
bad "elc compile failed:"; sed 's/^/ /' "$B/elc.err" | head
fi
# optional downstream consumer: forge builds on the SDK (make build) — proves the
# freshly-assembled el SDK still compiles a real downstream repo.
FORGE="$STACK_ROOT_WS/foundation/forge"
if [ -f "$FORGE/Makefile" ]; then
say "downstream consumer: foundation/forge (make build)"
( cd "$FORGE" && EL_REPO="$EL_REPO" PATH="$EL_REPO/lang/dist/platform:$PATH" make build ) \
&& ok "forge built against the sandbox SDK" || bad "forge build failed (see above)"
fi
say "el-stack build complete"
BUILD
else
cat >> "$ws/build.sh" <<'BUILD'
# ---- 1) engram (elc engram/src/server.el -> cc engram.c el_runtime.c), from the sandbox el ----
say "build engram from $EL_REPO/engram/src/server.el"
B="$(mktemp -d)"
if "$ELC" "$EL_REPO/engram/src/server.el" > "$B/engram.c" 2>"$B/elc.err"; then
ok "elc -> engram.c ($(wc -c <"$B/engram.c" | tr -d ' ') bytes)"
if cc -std=c11 -O2 -w -I "$RT" -o "$B/engram" \
"$B/engram.c" "$RT/el_runtime.c" -lcurl -lpthread -lm 2>"$B/cc.err"; then
ok "engram binary built: $(ls -lh "$B/engram" | awk '{print $5}')"
else
bad "engram cc link failed:"; grep -i 'error:' "$B/cc.err" | sort -u | head | sed 's/^/ /'
fi
else
bad "engram elc transpile failed:"; sed 's/^/ /' "$B/elc.err"
fi
# ---- 2) soul (imports ../foundation/el/elp/src/elp.el — resolves to SANDBOX el) ----
say "soul present + cross-repo import resolves inside the sandbox"
[ -f "$NEURON_REPO/soul.el" ] && ok "neuron/soul.el present" || bad "no soul.el"
if [ -f "$EL_REPO/elp/src/elp.el" ]; then
ok "../foundation/el/elp/src/elp.el resolves -> $EL_REPO/elp/src/elp.el (sandbox copy)"
else
bad "elp NLG source missing under sandbox el"
fi
# soul is a heavy single-TU compile; prove elc parses it rather than a full link
if "$ELC" "$NEURON_REPO/soul.el" > "$B/soul.c" 2>"$B/soul.err"; then
ok "elc compiled soul.el -> $(wc -c <"$B/soul.c" | tr -d ' ') bytes of C (cross-repo imports resolved)"
else
bad "soul.el elc compile failed:"; sed 's/^/ /' "$B/soul.err" | head
fi
# ---- 3) UI (present + buildable; gradle/JDK21 is heavy so we don't run it here) ----
say "app/UI present + buildable"
if [ -f "$NEURONUI_REPO/build.sh" ] || [ -f "$NEURONUI_REPO/gradlew" ]; then
ok "NeuronUI build entry present (./build.sh / ./gradlew — needs JDK21; run: cd $NEURONUI_REPO && ./gradlew run)"
else
bad "no NeuronUI build entry"
fi
say "neuron-stack build complete (engram compiled, soul compiled, UI present & buildable)"
BUILD
fi
chmod +x "$ws/build.sh"
}
_write_readme(){
local ws="$1" profile="$2" name="$3" branch="$4" el_ws="$5"
cat > "$ws/README.md" <<MD
# $profile / $name — combined stack workspace
Assembled by \`sandbox\`. Every constituent repo is a **git worktree** on branch
\`$branch\`, forked off its origin repo's committed HEAD, laid out at its natural
relative path so cross-repo \`../foundation/el\` imports resolve **inside this
workspace** (the sandbox el), never the live tree.
## Get in
\`\`\`bash
cd $ws
source .stack-env # EL_REPO + PATH now point at the sandbox el
./build.sh # build the stack together (or: sandbox build $profile $name)
\`\`\`
## Layout
__STACK_LAYOUT__
## Isolation
- Worktrees only; the live soul/engram (:$LIVE_SOUL_PORT / :$LIVE_ENGRAM_PORT) are never touched.
- To run against an **isolated engram**, delegate to \`nsbx\` (guards the live store/ports):
\`\`\`bash
nsbx create $profile-$name --source \$EL_REPO && nsbx up $profile-$name
\`\`\`
## Tear down
\`\`\`bash
sandbox down $profile $name # remove every worktree; branch '$branch' kept
sandbox down $profile $name --delete-branch
\`\`\`
MD
# fill the layout list from the manifest without embedding backticks in the heredoc
python3 - "$ws" <<'PY'
import json, os, sys
ws = sys.argv[1]
d = json.load(open(os.path.join(ws, ".stack-manifest.json")))
lines = ["- `%s` <- worktree of %s" % (r["rel"], r["origin"]) for r in d["repos"]]
p = os.path.join(ws, "README.md")
txt = open(p).read().replace("__STACK_LAYOUT__", "\n".join(lines))
open(p, "w").write(txt)
PY
}
# ================================================================ down =========
cmd_down(){
local profile="$1" name="$2"; shift 2 || true
local del_branch=0
while [ $# -gt 0 ]; do case "$1" in
--delete-branch) del_branch=1; shift;;
*) die "unknown flag: $1";;
esac; done
need git
local ws; ws="$(ws_dir "$profile" "$name")"
[ -d "$ws" ] || die "no such workspace: $ws"
local mf; mf="$(manifest "$ws")"
[ -f "$mf" ] || die "no manifest in $ws (refusing to guess); remove it by hand if intended"
local branch; branch="$(python3 -c "import json;print(json.load(open('$mf'))['branch'])")"
log "tearing down '$profile/$name' ($ws)"
# remove each worktree through its origin repo
python3 -c "import json;[print(r['origin']+'\t'+r['worktree']) for r in json.load(open('$mf'))['repos']]" \
| while IFS=$'\t' read -r origin wt; do
if [ -d "$wt" ]; then
git -C "$origin" worktree remove --force "$wt" 2>/dev/null || rm -rf "$wt"
git -C "$origin" worktree prune 2>/dev/null || true
ok "removed worktree: ${wt#$ws/}"
fi
if [ "$del_branch" -eq 1 ]; then
git -C "$origin" branch -D "$branch" 2>/dev/null && ok "deleted branch $branch in ${origin#$DEV_ROOT/}" || true
fi
done
# drop the (now worktree-free) workspace tree
rm -rf "$ws"
ok "workspace removed: $ws"
[ "$del_branch" -eq 1 ] || info "branch '$branch' kept in each repo (use --delete-branch to drop)"
ok "down '$profile/$name' complete (live untouched)"
}
# ================================================================ build ========
cmd_build(){
local profile="$1" name="$2"; local ws; ws="$(ws_dir "$profile" "$name")"
[ -x "$ws/build.sh" ] || die "no build.sh in $ws (is it assembled? sandbox $profile $name)"
exec "$ws/build.sh"
}
# ================================================================ list/status ==
cmd_list(){
[ -d "$STACK_ROOT" ] || { info "no stack workspaces (root $STACK_ROOT absent)"; return 0; }
local mf found=0
for mf in "$STACK_ROOT"/*/.stack-manifest.json; do
[ -f "$mf" ] || continue; found=1
python3 -c "import json;d=json.load(open('$mf'));print(' %-22s %-8s %2d repos branch=%s'%(d['profile']+'/'+d['name'],'',len(d['repos']),d['branch']))" 2>/dev/null
done
[ "$found" -eq 1 ] || info "no assembled stack workspaces under $STACK_ROOT"
}
cmd_status(){
local profile="$1" name="$2"; local ws; ws="$(ws_dir "$profile" "$name")"
local mf; mf="$(manifest "$ws")"; [ -f "$mf" ] || die "no such workspace: $ws"
log "stack '$profile/$name'"; info "workspace: $ws"
python3 - "$mf" <<'PY'
import json,sys,subprocess
d=json.load(open(sys.argv[1]))
print(f" branch: {d['branch']}")
for r in d['repos']:
st=subprocess.run(["git","-C",r["worktree"],"status","--porcelain"],capture_output=True,text=True).stdout
n=len([l for l in st.splitlines() if l.strip()])
print(f" {r['rel']:<20} {r['head'][:9]} {'clean' if n==0 else str(n)+' changed'}")
PY
}
# ================================================================ usage/main ===
usage(){ cat >&2 <<EOF
${C_BLD}sandbox${C_0} — assemble a WHOLE Neuron stack into one combined worktree workspace,
wired to build together on an isolated clean base. Multi-repo sibling of ${C_BLD}nsbx${C_0}.
${C_CYN}sandbox el-stack <name>${C_0} [--minimal] elc + EL language + el-ui framework + tooling (+ SDK consumers)
${C_CYN}sandbox neuron-stack <name>${C_0} [--minimal] runtime/soul + engram + app/UI (the full product)
${C_CYN}sandbox build <profile> <name>${C_0} build the assembled stack together (runs its build.sh)
${C_CYN}sandbox status <profile> <name>${C_0} inspect one workspace
${C_CYN}sandbox list${C_0} list assembled workspaces
${C_CYN}sandbox down <profile> <name>${C_0} [--delete-branch] tear down (remove worktrees; branch kept)
Flags: --minimal only the required repos --branch B branch name --base REF fork point
Env: NSBX_STACK_ROOT (workspace root, default \$DEV_ROOT/stack-worktrees) NEURON_DEV_ROOT
Each constituent repo becomes a git worktree at its natural relpath inside the
workspace, so cross-repo ../foundation/el imports resolve to the SANDBOX el. The
live soul/engram (:$LIVE_SOUL_PORT / :$LIVE_ENGRAM_PORT) are never touched; isolated-engram
bring-up is delegated to nsbx.
EOF
}
main(){
local cmd="${1:-}"; shift || true
case "$cmd" in
el-stack|neuron-stack) cmd_up "$cmd" "$@";;
up) [ $# -ge 1 ] || die "usage: sandbox up <profile> <name>"; local p="$1"; shift; cmd_up "$p" "$@";;
down) [ $# -ge 2 ] || die "usage: sandbox down <profile> <name>"; cmd_down "$@";;
build) [ $# -ge 2 ] || die "usage: sandbox build <profile> <name>"; cmd_build "$@";;
status) [ $# -ge 2 ] || die "usage: sandbox status <profile> <name>"; cmd_status "$@";;
list|ls) cmd_list "$@";;
""|-h|--help|help) usage;;
*) die "unknown command: $cmd (try: sandbox help)";;
esac
}
main "$@"