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.
86 lines
3.7 KiB
EmacsLisp
86 lines
3.7 KiB
EmacsLisp
// 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)
|
|
}
|