b7e2c580a8
El SDK CI - dev / build-and-test (pull_request) Successful in 6m28s
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.
137 lines
5.0 KiB
EmacsLisp
137 lines
5.0 KiB
EmacsLisp
// 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
|
|
}
|