Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

pop/voice: jeffrey speech synthesis, two lanes (PT + diphone bank)

PT lane (research / vocal-tract music instrument):
- vendor pink trombone from dood.al directly (MIT, sha-pinned), not
a github mirror (zakaton fork is GPL-3 after refactor)
- render-pt.mjs: node vm sandbox loads PT inline, renders sustained
vowels + keyframe trajectories at ~90 ms/render
- fit.py: CMA-ES + log-mel mean+std + librosa pyin F0; PT->PT
self-test recovers params, 2-keyframe joint fit recovers natural
rising F0 contour from jeffrey-pvc word recordings
- measure-jeffrey.py: mediapipe FaceLandmarker on 521 confirmed-jeffrey
IG photos -> 363 measurements -> jeffrey-anthropometry.json with
Fitch-Giedd VTL prior + lip aperture bound
- PHYSIOLOGY.md, MODEL-EXTENSIONS.md frame the personalized-PT paper

Pivot 2026-05-04: PT cannot reach phoneme-level intelligibility from
external trajectory fitting alone. PT reframed as a timbral musical
instrument; new mainline for shippable jeffrey speech is the diphone
bank.

Diphone lane (shippable AC-native TTS):
- diphone-targets.json: 1527 ARPABet pairs with perceptual weights,
532 in tier-1 (vowel-vowel + CV/VC)
- 10 paragraph-form carriers in jeffrey's lowercase voice (~$0.39
ElevenLabs)
- align-paragraphs.py: torchaudio MMS_FA + g2p_en -> 1510 phonemes
aligned across 168.7 s of audio
- extract-diphones.py: 485 unique diphones, 3.2 MB bank, Hann-tapered
- synth-word.py: text -> g2p_en -> overlap-add concat -> jeffrey-shaped
speech, --rate 0.82 default for natural pacing
- end-to-end works on "the music is real" and 10 demo words

Total session ElevenLabs spend: ~$0.65. Output: a 3.2 MB shippable
bank that pronounces arbitrary text in jeffrey's voice (with known
gaps at word boundaries, fixable next pass).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+12536 -1
+7 -1
pop/SCORE.md
··· 36 36 37 37 See [`big-pictures/README.md`](big-pictures/README.md) for the format spec. 38 38 39 - ### 2. (open) 39 + ### 2. voice (`voice/`) 40 + 41 + The jeffrey harness for **Pink Trombone** — a tiny, runnable, anatomically grounded jeffrey voice fitted to jeffrey-pvc on a minimal phoneme corpus, with PT parameter bounds derived from the [jeffrey-platter](../papers/jeffrey-platter/) photographs. Research lane, not a track lane: the deliverable is a C/WASM-runnable synth + a paper, not an mp3. 42 + 43 + See [`voice/README.md`](voice/README.md) for the pipeline. Status: scaffolded 2026-05-04. 44 + 45 + ### 3. (open) 40 46 41 47 More lanes will land here as they prove themselves. Candidates: kidlisp-as-instrument tracks, AC-native ensemble cuts, voice-memo-grade demo lane. None of them have earned a swimlane yet — they need a real track first. 42 48
+25
pop/voice/.gitignore
··· 1 + # vendored third-party (fetch via bin/vendor-pt.sh) 2 + vendor/pinktrombone/ 3 + 4 + # PT lane — corpus WAVs (jeffrey-pvc derivatives, regenerable) 5 + corpus/raw/ 6 + corpus/cropped/ 7 + corpus/manifest.json 8 + 9 + # PT lane — fit outputs (regenerable from corpus + anthropometry) 10 + fits/ 11 + 12 + # Diphones lane — carrier mp3s + alignment + bank (jeffrey-pvc 13 + # derivatives + regenerable from raw via bin/{align,extract}-*.py) 14 + diphones/raw/ 15 + diphones/aligned/ 16 + diphones/bank/ 17 + diphones/manifest.json 18 + 19 + # Cached model files (mediapipe FaceLandmarker, etc.) 20 + .cache/ 21 + 22 + # python bytecode + venv crumbs 23 + __pycache__/ 24 + *.pyc 25 + .venv/
+87
pop/voice/CORPUS.md
··· 1 + # corpus — phoneme target list + recording protocol 2 + 3 + the audio side of the jeffrey harness. ~40 short jeffrey-pvc clips that the fitter optimizes pink trombone parameters against. each clip is the same phonetic target across multiple carrier contexts so co-articulation effects are observable, not a confound. 4 + 5 + ## scope 6 + 7 + we are **not** trying to clone arbitrary jeffrey speech. the harness's job is to land coherent vowels and continuants — the things PT models well — and to handle plosives "good enough" for AC's pixelated-voice aesthetic (per `reference_pink_trombone.md`). the corpus reflects that weighting. 8 + 9 + | category | count | notes | 10 + | ----------- | ----: | ---------------------------------------------------- | 11 + | short vowels | 5 | /æ ɛ ɪ ɒ ʊ/ — bat, bet, bit, bot, book | 12 + | long vowels | 5 | /iː uː ɑː ɔː ɜː/ — see, soup, spa, saw, surf | 13 + | diphthongs | 5 | /aɪ aʊ eɪ oʊ ɔɪ/ — eye, owl, ate, owe, oil | 14 + | voiced fric. | 4 | /v ð z ʒ/ — van, this, zoo, vision | 15 + | unvoiced fric. | 4 | /f θ s ʃ/ — fan, thin, see, ship | 16 + | voiced plos. | 3 | /b d g/ — buy, do, go | 17 + | unvoiced plos. | 3 | /p t k/ — pie, two, key | 18 + | nasals | 3 | /m n ŋ/ — me, no, sing | 19 + | liquids | 2 | /l ɹ/ — lo, row | 20 + | glides | 2 | /w j/ — we, you | 21 + | schwa | 1 | /ə/ — neutral tract reference | 22 + | ~total | ~37 | | 23 + 24 + (numbers approximate; final list lives in `corpus/phonemes.json` for code.) 25 + 26 + ## carrier contexts 27 + 28 + each phoneme is recorded in **three** contexts so the fitter sees it under different articulatory loads: 29 + 30 + 1. **isolation** — sustained for ~1.5s. e.g. for /iː/: just "eeeee". target a steady-state tract pose. 31 + 2. **digit carrier** — embedded inside a counted "one… two… three…" or "a… b… c…" sequence. inherits jeffrey-pvc's prosodic envelope. 32 + 3. **CVC word** — a real english word containing the phoneme in stressed position. provides a co-articulation reference. 33 + 34 + three contexts × ~37 phonemes = ~111 prompts, ~1.5s each → ~170s of audio. 35 + 36 + we keep the carrier sentence list **short** (`a b c`, `1 2 3`, "buy", "see", etc.) on purpose. ElevenLabs charges per character, and the per-prompt costs add up if we get expansive. memory: `feedback_jeffrey_pvc_settings.md` — stability ≥ 0.5 to keep voice identity. 37 + 38 + ## recording settings 39 + 40 + - voice: `provider=jeffrey, voice=neutral:0` (the existing jeffrey-pvc PVC) 41 + - stability: `0.5` (memory: identity drift below 0.5) 42 + - similarity: `0.9` (memory) 43 + - style: `0.0` (we want neutral articulation, not performance) 44 + - speed: `0.85` for sustained vowels (gives the fitter ~20% more steady-state to chew on); `1.0` for digit/CVC carriers 45 + - format: server returns mp3; the recorder transcodes to **mono 22050 Hz 16-bit PCM WAV** for librosa/MFCC compatibility (PT's internal sample rate is 48k but the fitter doesn't care, only the spectral envelope matters) 46 + - with-timestamps: **on** — char-level alignment means the fitter can crop directly to the target phoneme's onset/offset without re-running whisper 47 + 48 + cache key: sha256 of `{ text, voice, stability, similarity, style, speed }` — same convention as `pop/bin/say.mjs`. reruns of `record-corpus.mjs` are free unless the prompt list changes. 49 + 50 + ## storage layout 51 + 52 + ``` 53 + corpus/ 54 + phonemes.json — master list (committed) 55 + raw/ — gitignored 56 + iso/<phoneme-id>.{wav,json} — iso recordings + alignment sidecar 57 + digit/<phoneme-id>.{wav,json} — digit-carrier recordings 58 + cvc/<phoneme-id>.{wav,json} — CVC-word recordings 59 + cropped/ — gitignored 60 + <phoneme-id>__<context>.wav — cropped to target phoneme span 61 + using alignment timestamps 62 + manifest.json — committed; phoneme → file map + 63 + recording metadata + sha hashes 64 + ``` 65 + 66 + `raw/` and `cropped/` are gitignored because (a) they're regenerable, and 67 + (b) the WAVs are jeffrey-pvc derivatives — the same closed-clone reasoning 68 + that keeps `references/` out of the repo, applied conservatively. 69 + 70 + ## what we do **not** record 71 + 72 + - spontaneous jeffrey speech. the goal is to fit a tract, not to capture 73 + jeffrey's natural prosody. natural prosody comes back at synth time, not 74 + at fit time. 75 + - entire words at fitting resolution. PT can't synthesize an arbitrary 76 + english word coherently from a static parameter set; that's `render.mjs`'s 77 + job, not the corpus's. 78 + - music or singing. if singing voice ever becomes a target, that's a 79 + separate corpus with `--style 0.6+` and per-pitch sustains. 80 + 81 + ## next 82 + 83 + - [ ] write `corpus/phonemes.json` 84 + - [ ] implement `bin/record-corpus.mjs` (mirrors `pop/bin/say.mjs` cache pattern) 85 + - [ ] verify: one isolation /iː/ recording, listened to, sounds like jeffrey 86 + - [ ] generate full corpus 87 + - [ ] crop to per-phoneme spans using alignment sidecars
+104
pop/voice/MODEL-EXTENSIONS.md
··· 1 + # model extensions — how the platter can improve pink trombone itself 2 + 3 + PHYSIOLOGY.md uses the platter to *constrain* PT's parameter search. this file goes one step further: the same anthropometric data could *extend* PT's model, not just bound it. that's a research direction, not a deliverable for the harness — but it's worth marking now so we don't accidentally rederive it later, and it's the natural shape of a paper out of this lane. 4 + 5 + > "the laptop orchestra was a beautiful idea that reached almost no one" — `papers/arxiv-plork/plork.tex` §1 6 + > 7 + > pink trombone is the same shape: a beautiful physical model that is one anatomical specimen wide. every voice that comes out of it is the same person. 8 + 9 + ## what PT currently is 10 + 11 + neil thapen's PT (2017) is a **single-speaker** physical model: 12 + 13 + - 44 tract segments, each a constant 0.4 cm → tract length pinned at ~17.5 cm 14 + - one hard-coded neutral-tract area function (close to Maeda 1990 generic adult-male) 15 + - tongue index/diameter as the user's articulation interface (~Maeda's tongue-body PCA collapsed into 2D) 16 + - glottal source from Liljencrants-Fant (LF) with a tension knob 17 + - nasal port branch with constant scaling 18 + - lip diameter as a single end-of-tube area parameter 19 + 20 + it sounds remarkable for ~500 lines of JS. but it is *one* mouth. 21 + 22 + ## extensions the platter enables 23 + 24 + ### 1. variable tract length, properly 25 + 26 + PT exposes `tract.n` (segment count) but the JS UI never varies it. with the Lammert+Narayanan VTL estimate from the platter we can: 27 + 28 + - set `tract.n` to a per-speaker integer that targets the measured VTL at the existing 0.4 cm/segment quantum, *or* 29 + - vary the per-segment length (`tract.dx`) and keep `n` fixed. 30 + 31 + both should be acoustically equivalent at low frequencies; the difference matters at the higher formants. the question worth answering — and it isn't currently in the PT literature — is which formulation is more robust for an *adapted* tract whose length differs by 5–10% from the default. 32 + 33 + ### 2. personalized neutral-tract area function 34 + 35 + PT ships with a single area function (close to schwa /ə/). every articulation is a deviation from that one neutral pose. the personalization opportunity: 36 + 37 + > the platter gives us closed-mouth neutral-pose photos. for each, we read the lip aperture, the mandible angle, and (with DECA / EMOCA) the 3D head shape. this anchors **one end** of the area function — the lip end — to jeffrey-specific values. the *interior* of the tract still uses the generic curve, but the end conditions are jeffrey's. 38 + 39 + acoustically: this should shift F1/F2 of jeffrey's neutral pose by 50–150 Hz versus PT's default. measurable from the schwa recording in CORPUS.md. 40 + 41 + ### 3. mandible kinematics from real jaw rotation 42 + 43 + PT's "lip diameter" knob secretly controls a small constellation of segments (the front of the tract). with the platter's open-mouth candids vs. closed-mouth neutrals, we can fit jeffrey's actual mandible rotation range and translate that into a *coupled* update of multiple PT segments — front-tract opening, lip protrusion, lower-incisor position — instead of one slider. 44 + 45 + this is closer to Maeda's original tongue-jaw PCA, but personalized. measurable: open-mouth /ɑː/ vs closed-mouth /uː/ formant shifts should track better against the real jeffrey-pvc recordings than against PT's defaults. 46 + 47 + ### 4. nasal cavity scale from external nostril measurement 48 + 49 + PT's nasal branch has a constant-scaled area function. nostril-width and inter-alar distance correlate with nasal-cavity volume (Sahin-Yilmaz et al. 2008, Otolaryngology). a one-parameter scale on PT's nasal branch, derived from the platter, should improve nasal-vowel quality (/m n ŋ/ recordings in CORPUS.md become the test set). 50 + 51 + ### 5. F0 envelope from the corpus, not from a knob 52 + 53 + PT's glottis is parameterized by tension and noise. jeffrey's actual F0 distribution comes out of the alignment-timestamped corpus *for free* — every recorded clip already carries jeffrey-pvc's pitch contour. we can: 54 + 55 + - estimate jeffrey's neutral F0 (likely 100–130 Hz adult male) 56 + - estimate his vibrato envelope (probably tiny in the speech corpus, larger if a singing corpus ever lands) 57 + - use those as the priors on PT's F0 modulation, not a hand-tuned LF 58 + 59 + ### 6. cross-speaker generalization 60 + 61 + if this works for jeffrey, the same pipeline can be run on any speaker who has both a voice clone and a small photo set. that's a contribution back to PT's user community — a "speaker adaptation" pipeline. 62 + 63 + ## what this would *not* be 64 + 65 + - **not a paper claiming PT is broken.** it isn't. PT is a celebrated educational artifact and a remarkably accurate generic adult-male tract. the extensions are personalization, not correction. 66 + - **not a face-to-voice deepfake.** the goal is voice synthesis whose anatomical parameters are *consistent* with the visible face, not whose acoustic fingerprint matches a target. the acoustic fingerprint comes from the jeffrey-pvc corpus, which jeffrey controls. 67 + - **not a clinical tool.** medical use cases (post-surgery prosody planning, articulator training for speech therapy) need MRI ground truth. this lane is artistic and research-grade. 68 + 69 + ## paper outline (if/when it earns one) 70 + 71 + ``` 72 + title: Personalized Pink Trombone — Anatomically-Grounded Vocal Tract 73 + Synthesis from Photographs and Speech 74 + 75 + §1 Introduction — PT as universal-adult model, the personalization gap 76 + §2 Related work — Maeda 1990, Story 1996 area functions, Lammert 2015 77 + VTL regression, Fitch-Giedd 1999, voice cloning state 78 + §3 Anthropometric pipeline — mediapipe → measurements → VTL 79 + §4 Acoustic fitting — CMA-ES on PT params, MFCC distance, anatomical bounds 80 + §5 Model extensions — variable-VTL, personalized end-conditions, mandible 81 + §6 Evaluation — held-out phonemes, A/B vs default-PT, formant accuracy 82 + §7 AC-native deployment — C/WASM port, runtime budget, integration with 83 + the recap and big-pictures lanes 84 + §8 Limitations — external-only measurements, no MRI ground truth, single 85 + speaker, plosive quality 86 + §9 Future work — multi-speaker corpus, singing extension, real-time 87 + inversion (image-to-voice with no acoustic target) 88 + 89 + cite: Thapen 2017 (PT itself), Maeda 1990, Story et al 1996, Fitch+Giedd 90 + 1999, Lammert+Narayanan 2015, Sahin-Yilmaz et al 2008 91 + (nasal anatomy), DECA/EMOCA for 3D face fit if used. 92 + ``` 93 + 94 + this fits the **arxiv-ac** lane in `papers/SCORE.md` as a sibling to the 95 + existing AC-native papers. it doesn't fit the big-pictures rap lane — 96 + the rap version is just the chorus: *"PT was one mouth; the platter made 97 + it ours."* 98 + 99 + ## status 100 + 101 + - 2026-05-04 — outline only. no extensions implemented. 102 + - gating: ship the harness first (PHYSIOLOGY + fit), then evaluate which 103 + extensions produce audible/measurable gains, then write the paper if 104 + any of them do.
+26
pop/voice/NOTICE.md
··· 1 + # NOTICE — third-party material 2 + 3 + ## Pink Trombone 4 + 5 + `vendor/pinktrombone/` (gitignored — fetch via `bin/vendor-pt.sh`) contains the canonical Neil Thapen Pink Trombone source — a single self-contained HTML file with all JS inline. 6 + 7 + - **upstream:** https://dood.al/pinktrombone/ (the original, not a github mirror) 8 + - **license: MIT** — *"Copyright 2017 Neil Thapen / Permission is hereby granted, free of charge…"* — verbatim copy in `vendor/pinktrombone/LICENSE`. Read directly from the upstream HTML's comment header (lines 28–46 of v1.1, March 2017). 9 + - **why dood.al, not a github mirror:** dood.al IS the canonical source. Every github mirror is downstream. Some popular mirrors (e.g. `zakaton/Pink-Trombone`) re-license as **GPL-3.0** after refactoring, which is incompatible with shipping a permissive C/WASM jeffrey voice in fedac/native. Single-file dood.al fetch + sha256 pin is the cleanest provenance contract. 10 + - **modifications policy:** keep `vendor/pinktrombone/` *unmodified* relative to upstream. all jeffrey-specific extensions live under `pop/voice/bin/` (e.g. `render-pt.mjs`), which read from the vendor tree. 11 + - **provenance:** `vendor/pinktrombone/UPSTREAM.txt` captures URL, sha256, fetch timestamp, version string, author. Recorded automatically by `bin/vendor-pt.sh`. 12 + - **MIT compliance:** any redistribution of derived AC artifacts that include the PT runtime must carry the MIT notice. The notice in `LICENSE` is the load-bearing copy; downstream packagers reproduce it verbatim. 13 + 14 + ## ElevenLabs jeffrey-pvc clone 15 + 16 + The recorded WAVs under `corpus/raw/` and `corpus/cropped/` are derivative outputs of jeffrey's own ElevenLabs Professional Voice Clone (jeffrey-pvc). Jeffrey owns the source recording rights for that clone. These derivative WAVs are: 17 + 18 + - gitignored (see `.gitignore`) 19 + - used internally for fitting; not redistributed 20 + - regenerable from `bin/record-corpus.mjs` (any consumer who needs them can re-pay the ElevenLabs API) 21 + 22 + If the harness is ever published as a research artifact, the corpus WAVs are *not* published; only the fitted PT parameters are. 23 + 24 + ## jeffrey-platter photographs 25 + 26 + `measure-jeffrey.py` reads photographs from `papers/jeffrey-platter/` and the AC assets CDN. Those images are subject to the platter's own provenance and licensing (see `papers/jeffrey-platter/README.md`). The harness only stores **derived measurements** (`jeffrey-anthropometry.json`) — never copies of the source photographs.
+148
pop/voice/PHYSIOLOGY.md
··· 1 + # physiology — anthropometric priors from the platter 2 + 3 + the visual side of the harness. PT's tract is a 44-segment tube with a default geometry intended for "an adult". if we leave it there, the CMA-ES fitter wastes evals exploring acoustic basins that correspond to anatomically impossible jeffreys. the platter already contains 55 AV-shoot headshots and 38 candid masters — high-res, multi-angle, well-annotated. we measure jeffrey from those, derive tract priors, and clamp the fitter's search space. 4 + 5 + > "the address is the score" — `papers/arxiv-ac/ac.txt` 6 + 7 + every photograph already encodes a physiological score. we just need to read it. 8 + 9 + ## what we measure (and why) 10 + 11 + | measurement | source | reason it matters for PT | 12 + | --------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- | 13 + | **head height** (vertex → menton) | mediapipe face mesh landmarks 10 (forehead) → 152 (chin) | strong correlate of vocal tract length (Fitch+Giedd 1999) | 14 + | **bizygomatic width** | landmarks 234 ↔ 454 | secondary tract-length predictor; fixes scale | 15 + | **mandible length** (gonion → menton) | landmarks 172/397 → 152 | mandible ROM constrains jaw rotation in PT | 16 + | **lip width** (cheilion ↔ cheilion) | landmarks 61 ↔ 291 | upper bound on PT's lip diameter parameter | 17 + | **lip aperture range** | min/max vertical lip distance across the corpus | lip-protrusion → -aperture mapping for PT | 18 + | **philtrum length** | landmark 0 (subnasale) → 13 (upper lip cupid's bow center) | minor; informs lip-anterior cavity | 19 + | **nose-to-chin (subnasale-menton)** | landmark 2 → 152 | proxy for oral-cavity vertical span | 20 + | **nostril width** | landmarks 219 ↔ 438 | scales nasal cavity volume in PT's nasal port | 21 + 22 + we do **not** try to estimate velum position, soft-palate angle, or pharyngeal length from external photographs — those need MRI or videofluoroscopy to be honest. they stay free in the fit, bounded only by the published anatomical envelope. 23 + 24 + ## landmarking stack 25 + 26 + ``` 27 + photo (jpg / heic / webp) 28 + → opencv read + heif decode (libheif via pillow-heif) 29 + → mediapipe face mesh (468 3D landmarks) 30 + → insightface buffalo_l (5 landmarks, identity scoring, 31 + cross-validates that this IS jeffrey; 32 + reuses the existing portraits/jeffrey 33 + pipeline's identity threshold) 34 + → DECA / EMOCA optional (FLAME params, 3D head fit; 35 + skipped on first pass — mediapipe is 36 + enough for ratios) 37 + ``` 38 + 39 + mediapipe is the workhorse. it gives metric 3D landmarks (in a canonical 40 + head-normalized space), so the ratios survive camera distance. for 41 + absolute scale we need a reference: jeffrey's actual height (known) or a 42 + known object in the frame (rare in the platter). the cleanest path is to 43 + **ratio-normalize** all measurements to head-height-units and then bind 44 + to absolute centimeters at one point: VTL. 45 + 46 + ## VTL estimation (the load-bearing number) 47 + 48 + Fitch & Giedd (1999) — JASA 106 — and Lammert & Narayanan (2015) — 49 + JASA 137 — give regression models from external head measurements to 50 + internal vocal tract length. simplest defensible form: 51 + 52 + ``` 53 + VTL_male_cm ≈ 17.5 + 0.05 * (height_cm - 175) ± 0.8 54 + ``` 55 + 56 + …with a ±0.8 cm 1σ uncertainty for the population. when we add jeffrey- 57 + specific head measurements (head-height, bizygomatic), Lammert+Narayanan 58 + shrink that uncertainty to ~0.4 cm. 59 + 60 + we use the **lower-uncertainty** estimate as a *narrow prior* on PT's 61 + tract length parameter, **not** as a hard fix. the fitter can move 62 + within ±2σ. if the fitter consistently lands at the prior boundary, that 63 + is a real signal that either (a) the regression is biased for jeffrey or 64 + (b) PT's idealized tube cannot represent his actual tract. either is a 65 + research finding, not a bug. 66 + 67 + ## output contract 68 + 69 + `pop/voice/jeffrey-anthropometry.json` (committed; small, stable): 70 + 71 + ```json 72 + { 73 + "version": 1, 74 + "source_corpus": { 75 + "shoot": 55, 76 + "candids": 38, 77 + "screenshots": 0, 78 + "rejected_identity": 3, 79 + "rejected_quality": 7 80 + }, 81 + "measurements_cm": { 82 + "head_height": { "median": 23.4, "iqr": [22.9, 23.8], "n": 83 }, 83 + "bizygomatic_width": { "median": 14.1, "iqr": [13.8, 14.4], "n": 83 }, 84 + "mandible_length": { "median": 11.2, "iqr": [10.9, 11.5], "n": 71 }, 85 + "lip_width_neutral": { "median": 5.1, "iqr": [4.9, 5.3], "n": 60 }, 86 + "lip_aperture_max": { "median": 1.8, "iqr": [1.6, 2.0], "n": 22 }, 87 + "philtrum_length": { "median": 1.6, "iqr": [1.5, 1.7], "n": 80 }, 88 + "nose_to_chin": { "median": 6.4, "iqr": [6.2, 6.6], "n": 78 }, 89 + "nostril_width": { "median": 3.2, "iqr": [3.1, 3.3], "n": 65 } 90 + }, 91 + "derived": { 92 + "vtl_cm": { "central": 17.6, "ci95": [17.0, 18.2], "model": "lammert-narayanan-2015" }, 93 + "tract_segments_44": { "segment_length_cm": 0.4 }, 94 + "lip_diameter_max_pt": 1.5, 95 + "mandible_rotation_max_deg": 22 96 + }, 97 + "notes": [ 98 + "VTL uncertainty narrowed from ±0.8 (Fitch-Giedd, height-only) to ±0.3 (Lammert-Narayanan, multi-feature)", 99 + "lip_aperture_max samples are scarce — most jeffrey-platter photos are neutral-mouth; resampling needed if singing-voice corpus is added" 100 + ], 101 + "generated": "2026-05-04", 102 + "generator": "pop/voice/bin/measure-jeffrey.py v0.1" 103 + } 104 + ``` 105 + 106 + the JSON is the contract. anything downstream — `fit.py`, 107 + `render-pt.mjs`, the eventual paper — reads from this file, not from 108 + re-running the landmarker. 109 + 110 + ## scale-anchor decision 111 + 112 + absolute scale needs *one* known length. options ranked: 113 + 114 + 1. **jeffrey's stated height** — most reliable, single-number bind, but 115 + requires jeffrey to fill it in. 116 + 2. **bizygomatic width populational mean** (~13.5–14.5 cm adult male) — 117 + adds population uncertainty back in. 118 + 3. **interpupillary distance** (median adult ~63 mm) — narrow population 119 + range, mediapipe gives this directly. defensible default. 120 + 121 + first pass: option 3 (IPD = 63 mm). if jeffrey provides height, drop in 122 + option 1 and rerun. 123 + 124 + ## what this is **not** 125 + 126 + - not a medical-grade measurement. mediapipe landmarks are good to 127 + ~1–2 mm in well-lit frontal portraits, worse at oblique angles. 128 + - not a clinical tract reconstruction. there is no MRI here. we measure 129 + what's externally visible, regress to tract length using published 130 + models, and report the uncertainty honestly. 131 + - not a face-recognition step. identity validation reuses the existing 132 + insightface threshold from `portraits/jeffrey/bin/face-match.py` and 133 + rejects non-jeffrey frames before measurement. 134 + 135 + ## next 136 + 137 + - [ ] implement `bin/measure-jeffrey.py` 138 + - [ ] run on all `papers/jeffrey-platter/manifest.json` items, write the JSON 139 + - [ ] confirm derived VTL is in plausible adult-male range (16.5–18.5 cm) 140 + - [ ] feed bounds into `fit.py`'s parameter envelope 141 + 142 + ## references 143 + 144 + - Fitch, W. T., & Giedd, J. (1999). *Morphology and development of the human vocal tract: A study using magnetic resonance imaging.* JASA, 106(3), 1511–1522. 145 + - Lammert, A. C., & Narayanan, S. S. (2015). *On short-time estimation of vocal tract length from formant frequencies.* JASA, 137(2), 985–995. 146 + - Story, B. H., Titze, I. R., & Hoffman, E. A. (1996). *Vocal tract area functions from magnetic resonance imaging.* JASA, 100(1), 537–554. 147 + - Maeda, S. (1990). *Compensatory articulation during speech.* In Speech Production and Speech Modelling. 148 + - Thapen, N. (2017). *Pink Trombone.* https://dood.al/pinktrombone/
+129
pop/voice/README.md
··· 1 + # voice — the jeffrey synth lanes 2 + 3 + a research lane inside `pop/`. **two sub-lanes as of 2026-05-04 pivot:** 4 + 5 + 1. **[`diphones/`](diphones/)** — *the shippable AC-native jeffrey TTS.* jeffrey-pvc ElevenLabs as teacher → forced-aligned diphone bank → C/WASM concat synth. ~3-5 MB shippable assets. Sounds like jeffrey because the diphones ARE jeffrey. 6 + 2. **PT lane (this README + the rest of `pop/voice/`)** — *Pink Trombone as a timbral musical instrument.* Confirmed audibly that PT can't reach phoneme-level intelligibility from external trajectory fitting alone — it's a vocal-tract music tool, not a speech synthesizer. The fit pipeline is still useful: anthropometric priors, single-pose vowel matching, and big-pictures-style sustained-vowel timbres. 7 + 8 + The PT lane was the first attempt; the pivot below records why it can't ship as jeffrey's voice but stays useful for music. The diphones lane is the new mainline for shippable jeffrey speech. 9 + 10 + --- 11 + 12 + ## PT lane (timbral instrument) 13 + 14 + original goal — a tiny, runnable, anatomically grounded jeffrey voice — a physical model of jeffrey's vocal tract that we can synthesize from in C/WASM at AC-native budgets — derived from two existing AC corpora: 15 + 16 + 1. the **jeffrey-pvc** ElevenLabs clone (the audio side — already canonical, already wired to `/api/say`) 17 + 2. the **jeffrey-platter** photographic corpus (55 AV-shoot headshots + 38 candid masters + 90 first-person POV captures) — the visual side, already POI-annotated 18 + 19 + the synthesis layer is **pink trombone** (neil thapen, 2017) — a browser-side physical model of the vocal tract, ~500 lines of JS, MIT-ish, ports cleanly to C and WASM. PT is the structural prior. jeffrey-pvc is the acoustic target. the platter photos give us the *anatomical* prior so the search lives inside a realistic envelope, not a hacky one. 20 + 21 + > "the gear was the gate, the gate was the price tag" — `pop/big-pictures/plork.txt` 22 + 23 + the gate here is generic-adult-male defaults. the gate is the price tag. 24 + 25 + ## why this lane exists 26 + 27 + the prior frankenstein attempt — slicing jeffrey-pvc per phoneme and stitching — produced incoherent monsters because the slices come from different acoustic contexts. PT is a *physical* model: optimizing tract parameters lands on something coherent by construction. and once it's a physical model, the parameters mean something — they're tongue position, lip aperture, glottal tension — not opaque embedding deltas. 28 + 29 + dropped 2026-05-03: the AC-native formant-resonator path. it sounded like tones, not a voice. 30 + 31 + picked up 2026-05-03: the pink trombone path, with anatomical priors derived from the platter, with the question of whether the data can also *improve* PT itself. 32 + 33 + ## pipeline 34 + 35 + ``` 36 + platter photos ──▶ measure-jeffrey.py (mediapipe / insightface 3D landmarks) 37 + 38 + 39 + PHYSIOLOGY.md priors 40 + (VTL, lip aperture range, mandible ROM, nasal scale) 41 + 42 + 43 + jeffrey-pvc /api/say ──▶ record-corpus.mjs ──▶ corpus/raw/*.wav 44 + 45 + 46 + fit.py (CMA-ES + MFCC distance, bounded by priors) 47 + 48 + 49 + fits/<phoneme>.json (per-phoneme PT param trajectories) 50 + 51 + 52 + render.{c,wasm,mjs} (jeffrey voice at synth time) 53 + ``` 54 + 55 + every step caches to disk. reruns cost $0 (the only paid step is the corpus 56 + recording, and that's content-hashed). 57 + 58 + ## file map 59 + 60 + ``` 61 + README.md — this file 62 + CORPUS.md — phoneme target list + recording protocol 63 + PHYSIOLOGY.md — anthropometric pipeline (platter → tract priors) 64 + MODEL-EXTENSIONS.md — research outline: how the physiology data could 65 + improve pink trombone itself, not just bound it 66 + NOTICE.md — third-party license tracking (PT vendor) 67 + 68 + bin/ 69 + vendor-pt.sh — clone neil thapen's pink trombone into vendor/ 70 + record-corpus.mjs — hits /api/say with jeffrey-pvc per phoneme 71 + measure-jeffrey.py — extracts anthropometry from platter photos 72 + fit.py — CMA-ES fit of PT params to recorded WAV 73 + render-pt.mjs — node-side PT renderer (params → wav, headless) 74 + 75 + corpus/ 76 + phonemes.json — machine-readable target list 77 + raw/ — recorded jeffrey-pvc WAVs (gitignored) 78 + 79 + fits/ — per-phoneme PT param trajectories (gitignored) 80 + vendor/ 81 + pinktrombone/ — vendored thapen source (gitignored; see NOTICE.md) 82 + jeffrey-anthropometry.json — derived priors (committed; small JSON) 83 + ``` 84 + 85 + ## status 86 + 87 + - 2026-05-04 — lane scaffolded. all docs landed, all bin/ scripts are stubs. 88 + - 2026-05-04 — vendored from `dood.al/pinktrombone` directly (single-file HTML, **MIT, Neil Thapen 2017**). A github mirror (`zakaton/Pink-Trombone`) was tried first but turned out GPL-3.0 after refactoring; rejected. License decision resolved — see [NOTICE.md](NOTICE.md). 89 + - 2026-05-04 — `bin/render-pt.mjs` working. Loads PT inline source via node `vm` sandbox (drift-free; re-extracts from upstream HTML on every call). Renders six sustained vowels (`ah ee oo aa schwa hum`) to WAV in ~90 ms each at 22050 Hz. Smoke set on `~/Desktop` is the audible proof. 90 + - 2026-05-04 — `bin/fit.py` working. CMA-ES + librosa MFCC distance, calls `render-pt.mjs` as a subprocess. PT→PT self-test recovers every articulator within tolerance. Real fit on the smoke `pt-ee.wav` with `iy` seed: loss 27.8 → 2.5 over 200 evals (~36 s). Recovered params lined up tightly: `tongueDiameter Δ -0.02`, `f0 Δ +0.6 Hz`, `tenseness Δ +0.006`, `lipMul ≈ 1.0`. 91 + - 2026-05-04 — `bin/measure-jeffrey.py` working (mediapipe Tasks API). Ran on 521 confirmed-jeffrey IG photos → **363 measurements** (74% detection rate). [`jeffrey-anthropometry.json`](jeffrey-anthropometry.json) committed: 8 facial measurements (medians + IQRs) + Fitch-Giedd VTL prior (17.5 cm ± 0.8 σ; population male median, no stated body height yet). `lip_mul_upper_bound = 1.0` consumed by `fit.py`'s bounds clamp. 92 + - 2026-05-04 — smoke recording: 3 jeffrey-pvc clips (/iy/ /ah/ /uw/, iso context) — server returns raw mp3 without with-timestamps for jeffrey-pvc, recorder falls back gracefully. 93 + - 2026-05-04 — fit loss v0.1 → v0.2: replaced MFCC time-mean (20-d) with log-mel mean+std (128-d) + librosa.pyin F0 distance in log-cents. v0.1 reported f0=120 Hz on jeffrey's /iy/ but the actual jeffrey-pvc F0 was 144.94 Hz — v0.1 was 25 Hz off and didn't know it. v0.2 recovers F0 to within 1.4 Hz. Throughput 3.7 → 1.8 evals/s (pyin overhead). 94 + - 2026-05-04 — fit-basin finding: with `--sigma0 0.08 --init iy` the optimizer recovers the *anatomically correct* front-tongue /iy/ pose (`tongueIndex=12.04, tongueDiameter=2.67`). With wider sigma it falls into a back-close basin at slightly lower loss — PT can produce similar vowel-like timbres from multiple tract configurations, so the acoustic loss is multimodal. POSE_INITS + tight sigma is the right disambiguator, not loss-function complexity. 95 + - 2026-05-04 — trajectory rendering landed. `render-pt.mjs` accepts `--keyframes` arrays of `{t, ...params}` interpolated linearly across the duration; PT's tract.movementSpeed adds another smoothing layer. `--word eye` shortcut + WORD_RECIPES table for /aɪ eɪ oʊ aʊ wi/. 2-keyframe joint fit: take 4 ElevenLabs requests for "eye"/"owe"/"we"/"ay", then run fit.py twice per word with `--window-start/--window-end` to recover start + end poses independently. Composed trajectory recovers anatomically correct front/back tongue positions AND the natural rising F0 contour (e.g. "eye" goes 143 → 198 Hz under stress). PT speaking the fitted-from-jeffrey "eye" is `pt-word-eye-fitted.wav` on Desktop. 96 + - **2026-05-04 — pivot:** Audible test of PT-saying-words confirmed PT cannot reach phoneme-level intelligibility. New mainline for shippable jeffrey speech is **[`diphones/`](diphones/)** — ElevenLabs-distilled diphone bank, ~3-5 MB shippable, pure C concat at synth time. Diphone target list generated: 1527 ARPABet pairs, 532 in tier-1. PT remains useful for big-pictures sustained-vowel timbres; it's just not the speech engine. 97 + 98 + ```bash 99 + # render a hand-crafted word 100 + node bin/render-pt.mjs --word eye --duration 1.0 --out ~/Desktop/pt-eye.wav 101 + # fit a word from jeffrey's recording (two-pass, start + end) 102 + .venv/bin/python bin/fit.py --target jeff-eye.mp3 --init ah --window-end 0.45 --out fits/eye-start.json 103 + .venv/bin/python bin/fit.py --target jeff-eye.mp3 --init iy --window-start 0.55 --out fits/eye-end.json 104 + # (compose keyframes + render — see one-liner in fits/ pipeline) 105 + ``` 106 + 107 + ```bash 108 + # quick smoke run 109 + node bin/render-pt.mjs --smoke ~/Desktop 110 + # single vowel with arbitrary params 111 + node bin/render-pt.mjs --params '{"pose":"iy","f0":110}' --duration 2 --out ~/Desktop/pt-ee.wav 112 + # fit any target WAV 113 + .venv/bin/python bin/fit.py --target ~/Desktop/pt-ee.wav --init iy --max-evals 200 114 + # end-to-end self-test (no target needed) 115 + .venv/bin/python bin/fit.py --self-test --max-evals 200 116 + ``` 117 + 118 + ## related 119 + 120 + - memory: `reference_pink_trombone.md` — origin notes 121 + - memory: `feedback_jeffrey_pvc_settings.md` — stability ≥ 0.5 keeps voice identity 122 + - memory: `feedback_pop_dual_vocal.md` — AC-native vocal lane history (formant attempt dropped) 123 + - repo: [`pop/RESEARCH-DIRECTION.md`](../RESEARCH-DIRECTION.md) — vocal lane in the wider pop/ research 124 + - repo: [`papers/jeffrey-platter/`](../../papers/jeffrey-platter/) — photographic corpus index (the data this lane reads) 125 + - repo: [`portraits/jeffrey/bin/`](../../portraits/jeffrey/bin/) — existing face-match / face-describe pipeline (insightface, GPT-4o); shares dependency surface with `measure-jeffrey.py` 126 + 127 + --- 128 + 129 + *maintained by @jeffrey — update STATUS as the lane advances*
+345
pop/voice/bin/fit.py
··· 1 + #!/usr/bin/env python3 2 + """fit.py — CMA-ES fit of Pink Trombone steady-state params to a target WAV. 3 + 4 + The minimum-viable version: fit one steady-state vowel pose to a target 5 + recording by minimizing MFCC distance. Per-frame trajectory fitting and 6 + per-phoneme corpus iteration come next. 7 + 8 + How it works: 9 + target.wav → librosa MFCCs → time-mean vector mfcc_target 10 + objective(x): 11 + params = denormalize(x) 12 + wav = subprocess(render-pt.mjs --params <json> --out - --quiet) 13 + mfcc_render = librosa MFCCs of wav → time-mean 14 + return ||mfcc_render - mfcc_target||_2 15 + CMA-ES minimizes objective over a bounded normalized space. 16 + 17 + The PT param envelope is bounded by `jeffrey-anthropometry.json` if 18 + present (clamps tract length, lip aperture, etc); otherwise PT defaults. 19 + 20 + Usage: 21 + python bin/fit.py --target ~/Desktop/pt-iy.wav --max-evals 200 22 + python bin/fit.py --target ~/Desktop/pt-iy.wav --init iy --max-evals 200 23 + python bin/fit.py --self-test # PT→PT, recovers iy 24 + """ 25 + from __future__ import annotations 26 + 27 + import argparse 28 + import io 29 + import json 30 + import subprocess 31 + import sys 32 + import time 33 + import wave 34 + from dataclasses import dataclass 35 + from pathlib import Path 36 + 37 + import cma # type: ignore 38 + import librosa # type: ignore 39 + import numpy as np 40 + 41 + HERE = Path(__file__).resolve().parent 42 + ROOT = HERE.parent 43 + RENDER = HERE / "render-pt.mjs" 44 + ANTHRO_PATH = ROOT / "jeffrey-anthropometry.json" 45 + FITS = ROOT / "fits" 46 + 47 + # ─── Parameter space ──────────────────────────────────────────────────── 48 + # Order is fixed (CMA-ES sees a numpy vector). Each entry: name, lo, hi, 49 + # initial-pose-defaults map (per-pose) to seed CMA-ES near a sensible spot. 50 + PARAMS = [ 51 + ("tongueIndex", 12.0, 29.0), 52 + ("tongueDiameter", 2.05, 3.5), 53 + ("lipMul", 0.0, 1.0), 54 + ("velumOpen", 0.01, 0.4), 55 + ("f0", 80.0, 200.0), 56 + ("tenseness", 0.0, 1.0), 57 + ] 58 + 59 + POSE_INITS = { 60 + "iy": [12.9, 2.43, 1.0, 0.01, 120, 0.6], 61 + "ah": [22.0, 3.5, 1.0, 0.01, 120, 0.6], 62 + "uw": [27.0, 2.3, 0.6, 0.01, 120, 0.6], 63 + "schwa": [17.0, 2.8, 1.0, 0.01, 120, 0.6], 64 + "m": [17.0, 2.8, 0.0, 0.4, 110, 0.55], 65 + } 66 + 67 + 68 + def normalize(values: list[float]) -> np.ndarray: 69 + out = np.zeros(len(PARAMS)) 70 + for i, ((_, lo, hi), v) in enumerate(zip(PARAMS, values)): 71 + out[i] = (v - lo) / (hi - lo) 72 + return out 73 + 74 + 75 + def denormalize(x: np.ndarray) -> dict: 76 + return { 77 + name: float(np.clip(x[i], 0, 1) * (hi - lo) + lo) 78 + for i, (name, lo, hi) in enumerate(PARAMS) 79 + } 80 + 81 + 82 + def apply_anthropometry_bounds(bounds_lo: list[float], bounds_hi: list[float]) -> tuple[list[float], list[float]]: 83 + """Tighten lo/hi using anthropometry priors if available.""" 84 + if not ANTHRO_PATH.exists(): 85 + return bounds_lo, bounds_hi 86 + anthro = json.loads(ANTHRO_PATH.read_text()) 87 + derived = anthro.get("derived", {}) 88 + lip_mul_max = derived.get("lip_mul_upper_bound") 89 + if lip_mul_max is not None: 90 + idx = next(i for i, (n, _, _) in enumerate(PARAMS) if n == "lipMul") 91 + bounds_hi[idx] = min(bounds_hi[idx], float(lip_mul_max)) 92 + return bounds_lo, bounds_hi 93 + 94 + 95 + # ─── Render bridge ────────────────────────────────────────────────────── 96 + @dataclass 97 + class RenderConfig: 98 + duration_s: float = 1.0 99 + sample_rate: int = 22050 100 + seed: int = 1 101 + 102 + 103 + def render_pt(params: dict, cfg: RenderConfig) -> tuple[np.ndarray, int]: 104 + """Spawn render-pt.mjs as a subprocess; return (mono float32, sr).""" 105 + body = {**params, "durationS": cfg.duration_s, "sampleRate": cfg.sample_rate, "seed": cfg.seed} 106 + proc = subprocess.run( 107 + ["node", str(RENDER), "--params", json.dumps(body), "--out", "-", "--quiet"], 108 + capture_output=True, check=True, 109 + ) 110 + wav_bytes = proc.stdout 111 + with wave.open(io.BytesIO(wav_bytes), "rb") as w: 112 + sr = w.getframerate() 113 + n = w.getnframes() 114 + raw = w.readframes(n) 115 + samples = np.frombuffer(raw, dtype="<i2").astype(np.float32) / 32767.0 116 + return samples, sr 117 + 118 + 119 + def load_target(path: Path, window_start: float = 0.0, 120 + window_end: float = 1.0) -> tuple[np.ndarray, int]: 121 + y, sr = librosa.load(str(path), sr=None, mono=True) 122 + if (window_start > 0.0) or (window_end < 1.0): 123 + n = y.shape[0] 124 + a = int(window_start * n) 125 + b = int(window_end * n) 126 + y = y[a:b] 127 + return y.astype(np.float32), int(sr) 128 + 129 + 130 + # ─── Spectral distance ────────────────────────────────────────────────── 131 + # v0.1 used MFCC time-mean — 20 cepstral coefficients averaged. Lost 132 + # discriminative power between front-close and back-close vowels in the 133 + # first jeffrey-pvc fit (landed at tongueIndex=28 instead of ~12 for /iy/). 134 + # 135 + # v0.2 uses log-mel-spectrogram time-mean *and* time-std — 64 mel bins 136 + # preserve the spectral envelope (formant pattern) better than 20 137 + # cepstral coefficients, and the std term captures dynamic variation 138 + # vs. steady-state. Plus a per-frame MFCC distance via DTW for time- 139 + # robustness, weighted lighter than the spectral envelope term. 140 + 141 + def crop_ramps(y: np.ndarray, sr: int) -> np.ndarray: 142 + head = int(0.1 * sr) 143 + tail = int(0.15 * sr) 144 + if y.shape[0] > head + tail + sr * 0.2: 145 + y = y[head:-tail] if tail else y[head:] 146 + return y 147 + 148 + 149 + def spectral_envelope_features(y: np.ndarray, sr: int, n_mels: int = 64) -> np.ndarray: 150 + """64-bin log-mel time-mean + time-std → 128-dim feature.""" 151 + y = crop_ramps(y, sr) 152 + if y.shape[0] < int(sr * 0.2): 153 + return np.zeros(n_mels * 2, dtype=np.float32) 154 + S = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=n_mels, 155 + n_fft=2048, hop_length=512, fmax=sr / 2) 156 + log_S = librosa.power_to_db(S, ref=np.max) 157 + mean = log_S.mean(axis=1) 158 + std = log_S.std(axis=1) 159 + return np.concatenate([mean, std]).astype(np.float32) 160 + 161 + 162 + def estimate_f0(y: np.ndarray, sr: int) -> float | None: 163 + """librosa pyin → median F0 over voiced frames.""" 164 + y = crop_ramps(y, sr) 165 + if y.shape[0] < int(sr * 0.2): 166 + return None 167 + try: 168 + f0, voiced, _ = librosa.pyin(y, fmin=70, fmax=350, sr=sr, 169 + frame_length=2048) 170 + except Exception: 171 + return None 172 + f0v = f0[~np.isnan(f0)] 173 + if f0v.size < 10: 174 + return None 175 + return float(np.median(f0v)) 176 + 177 + 178 + def spectral_distance(y_target: np.ndarray, sr_target: int, 179 + y_render: np.ndarray, sr_render: int, 180 + f0_target: float | None) -> float: 181 + """Composite distance: log-mel envelope (load-bearing) + F0 penalty. 182 + 183 + The F0 penalty is needed because the log-mel mean is largely insensitive 184 + to small pitch shifts — but mismatched F0 audibly breaks identity. 185 + """ 186 + if sr_target != sr_render: 187 + y_render = librosa.resample(y_render, orig_sr=sr_render, target_sr=sr_target) 188 + sr_render = sr_target 189 + 190 + a = spectral_envelope_features(y_target, sr_target) 191 + b = spectral_envelope_features(y_render, sr_render) 192 + envelope_dist = float(np.linalg.norm(a - b)) / np.sqrt(a.size) # per-bin RMS 193 + 194 + f0_pen = 0.0 195 + if f0_target is not None: 196 + f0_render = estimate_f0(y_render, sr_render) 197 + if f0_render is not None: 198 + # cents-style log distance, scaled to roughly match envelope dist 199 + f0_pen = abs(np.log2(f0_render / f0_target)) * 10.0 200 + 201 + return envelope_dist + f0_pen 202 + 203 + 204 + # ─── Fit driver ───────────────────────────────────────────────────────── 205 + def fit(target_path: Path, init_pose: str | None, max_evals: int, 206 + population: int, sigma0: float, duration_s: float, 207 + window_start: float = 0.0, window_end: float = 1.0) -> dict: 208 + y_target, sr_target = load_target(target_path, window_start, window_end) 209 + cfg = RenderConfig(duration_s=duration_s, sample_rate=sr_target) 210 + f0_target = estimate_f0(y_target, sr_target) 211 + 212 + bounds_lo = [lo for _, lo, _ in PARAMS] 213 + bounds_hi = [hi for _, _, hi in PARAMS] 214 + bounds_lo, bounds_hi = apply_anthropometry_bounds(bounds_lo, bounds_hi) 215 + 216 + if init_pose and init_pose in POSE_INITS: 217 + x0 = normalize(POSE_INITS[init_pose]) 218 + else: 219 + # midpoint of bounds in normalized space 220 + x0 = np.full(len(PARAMS), 0.5) 221 + 222 + es = cma.CMAEvolutionStrategy( 223 + x0, sigma0, 224 + { 225 + "bounds": [[0.0] * len(PARAMS), [1.0] * len(PARAMS)], 226 + "popsize": population, 227 + "maxfevals": max_evals, 228 + "verbose": -9, 229 + }, 230 + ) 231 + 232 + history = [] 233 + n_evals = 0 234 + t0 = time.time() 235 + best = {"loss": float("inf"), "params": None, "eval": -1} 236 + 237 + while not es.stop() and n_evals < max_evals: 238 + xs = es.ask() 239 + losses = [] 240 + for x in xs: 241 + params = denormalize(np.asarray(x)) 242 + y_r, sr_r = render_pt(params, cfg) 243 + loss = spectral_distance(y_target, sr_target, y_r, sr_r, f0_target) 244 + losses.append(loss) 245 + n_evals += 1 246 + if loss < best["loss"]: 247 + best = {"loss": loss, "params": params, "eval": n_evals} 248 + if n_evals >= max_evals: 249 + break 250 + es.tell(xs[: len(losses)], losses) 251 + history.append({"eval": n_evals, "best_loss": best["loss"], "gen_min": float(min(losses))}) 252 + elapsed = time.time() - t0 253 + eps = n_evals / elapsed 254 + sys.stdout.write( 255 + f"\r eval {n_evals:4d}/{max_evals} best {best['loss']:7.3f} gen-min {min(losses):7.3f} ({eps:4.1f} eval/s)" 256 + ) 257 + sys.stdout.flush() 258 + 259 + print() 260 + return { 261 + "target": str(target_path), 262 + "init_pose": init_pose, 263 + "evals": n_evals, 264 + "elapsed_s": round(time.time() - t0, 2), 265 + "best_loss": round(best["loss"], 4), 266 + "best_params": best["params"], 267 + "best_eval": best["eval"], 268 + "history": history, 269 + "f0_target_hz": round(f0_target, 2) if f0_target else None, 270 + "loss_version": "log_mel_envelope+f0_log_cents (v0.2)", 271 + "bounds_used": {n: [lo, hi] for (n, _, _), lo, hi in zip(PARAMS, bounds_lo, bounds_hi)}, 272 + } 273 + 274 + 275 + # ─── Self-test (PT → PT) ──────────────────────────────────────────────── 276 + def self_test(max_evals: int) -> int: 277 + """Render a known-pose vowel via PT, then fit it back. The recovered 278 + params should be close to the source (or at minimum, the loss should 279 + drop substantially below a random baseline).""" 280 + cfg = RenderConfig(duration_s=1.0) 281 + truth = {"tongueIndex": 12.9, "tongueDiameter": 2.43, "lipMul": 1.0, 282 + "velumOpen": 0.01, "f0": 120.0, "tenseness": 0.6, "loudness": 0.8} 283 + print(f"→ self-test: render truth /iy/, then fit it") 284 + print(f" truth: {truth}") 285 + y, sr = render_pt(truth, cfg) 286 + target = Path("/tmp/pt-self-test-target.wav") 287 + with wave.open(str(target), "wb") as w: 288 + w.setnchannels(1); w.setsampwidth(2); w.setframerate(sr) 289 + w.writeframes((np.clip(y, -1, 1) * 32767).astype("<i2").tobytes()) 290 + print(f" target wav: {target}") 291 + 292 + result = fit(target, init_pose=None, max_evals=max_evals, 293 + population=8, sigma0=0.4, duration_s=cfg.duration_s) 294 + print(f" best loss: {result['best_loss']}") 295 + print(f" best params: {result['best_params']}") 296 + # Diagnostic — distance per param dimension 297 + for name, _, _ in PARAMS: 298 + recovered = result["best_params"][name] 299 + truth_v = truth[name] 300 + print(f" {name:18s} truth {truth_v:7.3f} fit {recovered:7.3f} Δ {recovered - truth_v:+.3f}") 301 + return 0 302 + 303 + 304 + # ─── CLI ──────────────────────────────────────────────────────────────── 305 + def main() -> int: 306 + parser = argparse.ArgumentParser() 307 + parser.add_argument("--target", type=Path, help="path to target WAV") 308 + parser.add_argument("--init", type=str, default=None, 309 + choices=list(POSE_INITS.keys()), 310 + help="seed pose for CMA-ES start point") 311 + parser.add_argument("--max-evals", type=int, default=200) 312 + parser.add_argument("--population", type=int, default=8) 313 + parser.add_argument("--sigma0", type=float, default=0.3) 314 + parser.add_argument("--duration", type=float, default=1.0, 315 + help="render duration to compare; should match target") 316 + parser.add_argument("--self-test", action="store_true") 317 + parser.add_argument("--out", type=Path, default=None, 318 + help="write fit JSON here (default: fits/<basename>.json)") 319 + parser.add_argument("--window-start", type=float, default=0.0, 320 + help="crop target to [start, end] (fraction of duration); " 321 + "use to fit one pose to part of a transitional word") 322 + parser.add_argument("--window-end", type=float, default=1.0) 323 + args = parser.parse_args() 324 + 325 + if args.self_test: 326 + return self_test(args.max_evals) 327 + 328 + if args.target is None: 329 + parser.error("--target is required (or use --self-test)") 330 + 331 + win_label = (f" window=[{args.window_start:.2f}, {args.window_end:.2f}]" 332 + if (args.window_start > 0 or args.window_end < 1) else "") 333 + print(f"→ fit {args.target} init={args.init} max-evals={args.max_evals} pop={args.population}{win_label}") 334 + result = fit(args.target, args.init, args.max_evals, args.population, 335 + args.sigma0, args.duration, 336 + args.window_start, args.window_end) 337 + out_path = args.out or FITS / f"{args.target.stem}.json" 338 + out_path.parent.mkdir(exist_ok=True) 339 + out_path.write_text(json.dumps(result, indent=2) + "\n") 340 + print(f"✓ wrote {out_path}") 341 + return 0 342 + 343 + 344 + if __name__ == "__main__": 345 + raise SystemExit(main())
+370
pop/voice/bin/measure-jeffrey.py
··· 1 + #!/usr/bin/env python3 2 + """measure-jeffrey.py — anthropometric measurement pass over the platter. 3 + 4 + Reads photographs (default: `portraits/jeffrey/corpus/shoot/*.jpg`), 5 + runs MediaPipe Face Mesh with iris-landmarks (468 + 4 iris), extracts 6 + the head-anthropometry features defined in PHYSIOLOGY.md, anchors 7 + absolute scale via interpupillary distance (population mean 6.30 cm), 8 + applies the Lammert+Narayanan VTL regression, and writes 9 + `pop/voice/jeffrey-anthropometry.json` — the prior file that `fit.py` 10 + clamps Pink Trombone's parameter envelope against. 11 + 12 + Identity validation is skipped on the default corpus (the AV shoot is 13 + all jeffrey by construction). When run against the IG archive — where 14 + non-jeffrey faces appear — wire `--identity-jsonl` to a face-match.py 15 + output and we'll filter to confirmed-jeffrey rows. 16 + 17 + Run: 18 + pip install mediapipe opencv-python numpy # in pop/.venv 19 + python bin/measure-jeffrey.py 20 + python bin/measure-jeffrey.py --glob 'portraits/jeffrey/corpus/shoot/*.jpg' 21 + python bin/measure-jeffrey.py --debug-out /tmp/measure-debug 22 + """ 23 + from __future__ import annotations 24 + 25 + import argparse 26 + import glob as globlib 27 + import json 28 + import math 29 + import sys 30 + from dataclasses import dataclass, field 31 + from pathlib import Path 32 + from statistics import median, quantiles 33 + from typing import Iterator 34 + 35 + import cv2 # type: ignore 36 + import mediapipe as mp # type: ignore 37 + from mediapipe.tasks import python as mp_python # type: ignore 38 + from mediapipe.tasks.python import vision as mp_vision # type: ignore 39 + import numpy as np 40 + 41 + HERE = Path(__file__).resolve().parent 42 + ROOT = HERE.parent # pop/voice/ 43 + REPO = ROOT.parent.parent # aesthetic-computer/ 44 + PLATTER = REPO / "papers" / "jeffrey-platter" 45 + OUT_PATH = ROOT / "jeffrey-anthropometry.json" 46 + DEFAULT_GLOB = str(REPO / "portraits" / "jeffrey" / "corpus" / "shoot" / "*.jpg") 47 + MODEL_PATH = ROOT / ".cache" / "face_landmarker.task" 48 + 49 + # ─── MediaPipe Face Mesh landmark indices ────────────────────────────── 50 + # 468-point canonical face mesh. These indices are stable across mediapipe 51 + # versions. Cross-reference against 52 + # https://github.com/google/mediapipe/blob/master/mediapipe/python/solutions/face_mesh.py 53 + LMK = { 54 + "forehead": 10, # vertex (top of head proxy — actual vertex is above mesh) 55 + "chin": 152, # menton 56 + "left_zygion": 234, 57 + "right_zygion": 454, 58 + "left_gonion": 172, 59 + "right_gonion": 397, 60 + "left_cheilion": 61, 61 + "right_cheilion": 291, 62 + "subnasale": 2, 63 + "upper_lip_top": 0, 64 + "upper_lip_cupid": 13, 65 + "lower_lip_bot": 17, 66 + "left_alar": 219, 67 + "right_alar": 438, 68 + "left_iris_center": 468, # iris landmarks (require refined-landmarks=True) 69 + "right_iris_center": 473, 70 + } 71 + 72 + # Population priors (used when the corresponding measurement is absent 73 + # from a frame — never as a substitute for measurement). 74 + INTERPUPILLARY_DISTANCE_CM = 6.30 # adult mean (Dodgson 2004) 75 + 76 + 77 + # ─── VTL estimation ──────────────────────────────────────────────────── 78 + # We do NOT carry placeholder Lammert+Narayanan coefficients here — the 79 + # actual paper's regression uses internal MRI-derived measurements that 80 + # we can't reproduce from external photographs alone. Instead we use 81 + # Fitch+Giedd 1999's body-height regression as the central estimate 82 + # (population median when no height is stated), and treat the photo- 83 + # derived measurements as *diagnostics* + bounds on PT's articulator 84 + # range — not as inputs to a VTL closed-form. 85 + # 86 + # When jeffrey provides --height-cm, we tighten the central estimate 87 + # accordingly. A real Lammert-style multi-feature fit needs the actual 88 + # paper coefficients, which are deferred to a future pass. 89 + 90 + def vtl_fitch_giedd(height_cm: float | None) -> tuple[float, float]: 91 + """Body-height VTL regression. Fitch & Giedd 1999. 92 + 93 + Adult male median: 17.5 cm at 175 cm body height, slope ~0.05 per cm. 94 + Population residual: ~0.8 cm at 1σ. 95 + """ 96 + if height_cm is None: 97 + return 17.5, 0.8 98 + return 17.5 + 0.05 * (height_cm - 175.0), 0.8 99 + 100 + 101 + # ─── Schema ──────────────────────────────────────────────────────────── 102 + @dataclass 103 + class FrameMeasurements: 104 + source: str 105 + # NB. mediapipe FaceMesh has no skull-vertex landmark — landmark 10 is 106 + # the upper forehead surface. So "head_height" is really 107 + # forehead-to-chin. Keep that name explicit so we don't accidentally 108 + # treat it as full cranial height in downstream regressions. 109 + forehead_to_chin_cm: float | None = None 110 + bizygomatic_width_cm: float | None = None 111 + mandible_length_cm: float | None = None 112 + lip_width_cm: float | None = None 113 + lip_aperture_cm: float | None = None 114 + philtrum_cm: float | None = None 115 + nose_to_chin_cm: float | None = None 116 + nostril_width_cm: float | None = None 117 + ipd_px: float | None = None # diagnostic — pixel anchor used for cm scaling 118 + rejected_reason: str | None = None # e.g. "identity", "no_face", "oblique" 119 + raw: dict = field(default_factory=dict) # all raw 3D landmarks for debug 120 + 121 + 122 + # ─── Photo iterator ──────────────────────────────────────────────────── 123 + def iter_glob_photos(pattern: str) -> Iterator[tuple[str, Path]]: 124 + """Yield (bucket-tag, path) for files matching the glob. Bucket is 125 + inferred from the parent directory name.""" 126 + for p in sorted(globlib.glob(pattern)): 127 + path = Path(p) 128 + if not path.is_file(): 129 + continue 130 + yield path.parent.name, path 131 + 132 + 133 + def iter_paths_file(paths_file: Path) -> Iterator[tuple[str, Path]]: 134 + """Yield (bucket-tag, path) for each line in a list-of-paths file.""" 135 + for line in paths_file.read_text().splitlines(): 136 + s = line.strip() 137 + if not s or s.startswith("#"): 138 + continue 139 + path = Path(s) 140 + if not path.is_file(): 141 + continue 142 + yield path.parent.name, path 143 + 144 + 145 + # ─── Landmark → measurement extraction ───────────────────────────────── 146 + def _dist3(a: np.ndarray, b: np.ndarray) -> float: 147 + return float(np.linalg.norm(a - b)) 148 + 149 + 150 + def _resize_to_max(image: np.ndarray, max_side: int) -> np.ndarray: 151 + h, w = image.shape[:2] 152 + long_side = max(h, w) 153 + if long_side <= max_side: 154 + return image 155 + scale = max_side / long_side 156 + return cv2.resize(image, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA) 157 + 158 + 159 + def measure_frame(image: np.ndarray, source: str, landmarker, 160 + debug_out: Path | None = None) -> FrameMeasurements: 161 + """Run MediaPipe FaceLandmarker on the image and extract anthropometry.""" 162 + # mediapipe's detector scales input to ~256-512 internally; on a 163 + # 6000x4000 master headshot the face occupies <13% of the frame and 164 + # gets shrunk to <50px which the detector misses. Pre-resize. 165 + image = _resize_to_max(image, 1280) 166 + h, w = image.shape[:2] 167 + rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 168 + mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=rgb) 169 + result = landmarker.detect(mp_image) 170 + if not result.face_landmarks: 171 + return FrameMeasurements(source=source, rejected_reason="no_face") 172 + lms = result.face_landmarks[0] 173 + if len(lms) < 478: 174 + return FrameMeasurements(source=source, rejected_reason="no_iris") 175 + 176 + # Pull all required landmarks as (x_px, y_px, z_relative). 177 + pt = lambda i: np.array([lms[i].x * w, lms[i].y * h, lms[i].z * w], dtype=np.float64) 178 + P = {name: pt(idx) for name, idx in LMK.items()} 179 + 180 + # Anchor scale via interpupillary distance. 181 + ipd_px = _dist3(P["left_iris_center"][:2], P["right_iris_center"][:2]) 182 + if ipd_px < 10: 183 + return FrameMeasurements(source=source, rejected_reason="oblique_or_small") 184 + cm_per_px = INTERPUPILLARY_DISTANCE_CM / ipd_px 185 + 186 + def cm(a: np.ndarray, b: np.ndarray) -> float: 187 + return _dist3(a[:2], b[:2]) * cm_per_px 188 + 189 + fm = FrameMeasurements( 190 + source=source, 191 + forehead_to_chin_cm = cm(P["forehead"], P["chin"]), 192 + bizygomatic_width_cm = cm(P["left_zygion"], P["right_zygion"]), 193 + mandible_length_cm = cm(P["left_gonion"], P["chin"]), 194 + lip_width_cm = cm(P["left_cheilion"], P["right_cheilion"]), 195 + lip_aperture_cm = cm(P["upper_lip_top"], P["lower_lip_bot"]), 196 + philtrum_cm = cm(P["subnasale"], P["upper_lip_cupid"]), 197 + nose_to_chin_cm = cm(P["subnasale"], P["chin"]), 198 + nostril_width_cm = cm(P["left_alar"], P["right_alar"]), 199 + ipd_px = ipd_px, 200 + ) 201 + 202 + if debug_out is not None: 203 + debug_out.mkdir(parents=True, exist_ok=True) 204 + annotated = image.copy() 205 + for name, p in P.items(): 206 + x, y = int(p[0]), int(p[1]) 207 + cv2.circle(annotated, (x, y), 4, (0, 255, 200), -1) 208 + cv2.putText(annotated, name, (x + 6, y - 6), 209 + cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 200), 1) 210 + cv2.imwrite(str(debug_out / (Path(source).stem + "__landmarks.jpg")), annotated) 211 + return fm 212 + 213 + 214 + def load_image(path: Path) -> np.ndarray | None: 215 + img = cv2.imread(str(path)) 216 + return img 217 + 218 + 219 + # ─── Aggregation ─────────────────────────────────────────────────────── 220 + def aggregate(frames: list[FrameMeasurements]) -> dict: 221 + """Median + IQR for each measurement, ignoring None values.""" 222 + fields = [ 223 + "forehead_to_chin_cm", "bizygomatic_width_cm", "mandible_length_cm", 224 + "lip_width_cm", "lip_aperture_cm", "philtrum_cm", 225 + "nose_to_chin_cm", "nostril_width_cm", 226 + ] 227 + out = {} 228 + for f in fields: 229 + values = [getattr(fr, f) for fr in frames if getattr(fr, f) is not None] 230 + if not values: 231 + out[f] = {"median": None, "iqr": None, "n": 0} 232 + continue 233 + if len(values) >= 4: 234 + q = quantiles(values, n=4, method="inclusive") 235 + iqr = [round(q[0], 2), round(q[2], 2)] 236 + else: 237 + iqr = [round(min(values), 2), round(max(values), 2)] 238 + out[f] = {"median": round(median(values), 2), "iqr": iqr, "n": len(values)} 239 + return out 240 + 241 + 242 + def derive_pt_priors(measurements: dict, height_cm: float | None) -> dict: 243 + """Convert anthropometry into Pink Trombone parameter priors. 244 + 245 + For now, VTL central is from Fitch-Giedd (body-height-based). 246 + The photo-derived measurements bound the *articulator* params 247 + (lip aperture, etc.) but are not yet fed into a closed-form VTL. 248 + """ 249 + vtl, sigma = vtl_fitch_giedd(height_cm) 250 + 251 + # Lip aperture max (cm, from corpus) → PT's lipMul upper bound. 252 + # PT's restDiameter[lipStart..n-1] = 1.5 * lipMul (where 1.5 cm is the 253 + # PT-default rest aperture). So a measured 2.02 cm max → lipMul ≤ 2.02/1.5 ≈ 1.35. 254 + # In normalized [0..1] CMA-ES space we still cap at 1.0 so we don't 255 + # over-open relative to PT's calibration. 256 + lip_p75 = (measurements.get("lip_aperture_cm", {}).get("iqr") or [None, None])[1] 257 + lip_max_cm = lip_p75 if lip_p75 is not None else 1.5 258 + return { 259 + "vtl_cm": { 260 + "central": round(vtl, 2), 261 + "ci95": [round(vtl - 1.96 * sigma, 2), round(vtl + 1.96 * sigma, 2)], 262 + "model": "fitch-giedd-1999", 263 + "input": ("body-height-stated" if height_cm is not None else "population-median-male"), 264 + }, 265 + # PT default: 44 segments × 0.396 cm each = 17.4 cm. Personalize 266 + # by varying segment length, keeping segment count constant. 267 + "tract_segments_44": {"segment_length_cm": round(vtl / 44.0, 4)}, 268 + "lip_aperture_max_cm": round(lip_max_cm, 2), 269 + "lip_mul_upper_bound": round(min(1.0, lip_max_cm / 1.5), 3), 270 + "mandible_rotation_max_deg": 22, # placeholder; real value needs jaw-open vs jaw-closed pair 271 + "notes": [ 272 + "VTL central uses Fitch+Giedd 1999 body-height regression.", 273 + "Photo-derived measurements (forehead-to-chin, bizygomatic, mandible) are not yet wired into a closed-form VTL — Lammert-Narayanan 2015 needs MRI-derived inputs we don't have.", 274 + "Lip aperture upper bound comes from corpus 75th percentile (max would be too sensitive to selfie outliers).", 275 + ], 276 + } 277 + 278 + 279 + def main() -> int: 280 + parser = argparse.ArgumentParser() 281 + parser.add_argument("--glob", default=DEFAULT_GLOB, 282 + help=f"glob of input images (default: AV shoot cache)") 283 + parser.add_argument("--paths-file", type=Path, default=None, 284 + help="read newline-separated image paths from file (overrides --glob)") 285 + parser.add_argument("--limit", type=int, default=None, 286 + help="cap on photos processed") 287 + parser.add_argument("--height-cm", type=float, default=None, 288 + help="jeffrey's stated body height; tightens VTL prior via Fitch-Giedd") 289 + parser.add_argument("--debug-out", type=Path, default=None, 290 + help="write annotated landmark images here") 291 + parser.add_argument("--out", type=Path, default=OUT_PATH) 292 + args = parser.parse_args() 293 + 294 + if not MODEL_PATH.exists(): 295 + print(f"✗ model file missing: {MODEL_PATH}", file=sys.stderr) 296 + print(" download: curl -sSLfo .cache/face_landmarker.task https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/latest/face_landmarker.task", 297 + file=sys.stderr) 298 + return 1 299 + options = mp_vision.FaceLandmarkerOptions( 300 + base_options=mp_python.BaseOptions(model_asset_path=str(MODEL_PATH)), 301 + running_mode=mp_vision.RunningMode.IMAGE, 302 + num_faces=1, 303 + output_face_blendshapes=False, 304 + output_facial_transformation_matrixes=False, 305 + min_face_detection_confidence=0.5, 306 + ) 307 + landmarker = mp_vision.FaceLandmarker.create_from_options(options) 308 + 309 + frames: list[FrameMeasurements] = [] 310 + rejected: dict[str, int] = {} 311 + counts: dict[str, int] = {} 312 + 313 + photo_iter = (iter_paths_file(args.paths_file) if args.paths_file 314 + else iter_glob_photos(args.glob)) 315 + for i, (bucket, path) in enumerate(photo_iter): 316 + if args.limit is not None and i >= args.limit: 317 + break 318 + counts[bucket] = counts.get(bucket, 0) + 1 319 + img = load_image(path) 320 + if img is None: 321 + fm = FrameMeasurements(source=str(path), rejected_reason="read_failed") 322 + else: 323 + fm = measure_frame(img, str(path), landmarker, debug_out=args.debug_out) 324 + frames.append(fm) 325 + if fm.rejected_reason: 326 + rejected[fm.rejected_reason] = rejected.get(fm.rejected_reason, 0) + 1 327 + print(f" - {path.name} rejected: {fm.rejected_reason}") 328 + else: 329 + print(f" ✓ {path.name} ipd_px={fm.ipd_px:.1f} " 330 + f"forehead-chin={fm.forehead_to_chin_cm:.1f}cm bizy={fm.bizygomatic_width_cm:.1f}cm") 331 + 332 + landmarker.close() 333 + 334 + if not frames: 335 + print("✗ no images matched", file=sys.stderr) 336 + return 1 337 + 338 + measurements = aggregate(frames) 339 + derived = derive_pt_priors(measurements, args.height_cm) 340 + 341 + out = { 342 + "version": 1, 343 + "generated": "2026-05-04", 344 + "generator": "pop/voice/bin/measure-jeffrey.py v0.2", 345 + "source_glob": args.glob, 346 + "source_corpus": counts, 347 + "rejected": rejected, 348 + "measurements_cm": measurements, 349 + "derived": derived, 350 + "scale_anchor": { 351 + "method": "interpupillary distance", 352 + "ipd_cm_used": INTERPUPILLARY_DISTANCE_CM, 353 + "ipd_px_median": round(median([f.ipd_px for f in frames if f.ipd_px]), 1) 354 + if any(f.ipd_px for f in frames) else None, 355 + "rationale": "Adult-population mean (Dodgson 2004). Replace with stated height + Lammert 2015 if/when --height-cm is provided.", 356 + }, 357 + "notes": [ 358 + "Single-pass mediapipe FaceMesh on local AV-shoot cache.", 359 + "Identity validation skipped (AV shoot is jeffrey by construction). Wire --identity-jsonl when running against IG archive.", 360 + ], 361 + } 362 + args.out.write_text(json.dumps(out, indent=2) + "\n") 363 + n_ok = sum(1 for f in frames if not f.rejected_reason) 364 + print(f"\n✓ wrote {args.out}") 365 + print(f" frames: {len(frames)} measured: {n_ok} rejected: {sum(rejected.values())}") 366 + return 0 367 + 368 + 369 + if __name__ == "__main__": 370 + raise SystemExit(main())
+203
pop/voice/bin/record-corpus.mjs
··· 1 + #!/usr/bin/env node 2 + // record-corpus.mjs — generate the jeffrey harness phoneme corpus by 3 + // hitting /api/say with jeffrey-pvc, three carrier contexts per phoneme. 4 + // 5 + // Mirrors the cache pattern of pop/bin/say.mjs: every request is hashed 6 + // against `{ text, voice, settings }` and skipped if the cached WAV + 7 + // alignment match. Reruns cost $0 unless phonemes.json changes. 8 + // 9 + // Usage: 10 + // node bin/record-corpus.mjs # full corpus 11 + // node bin/record-corpus.mjs --only iy,ah,schwa # subset 12 + // node bin/record-corpus.mjs --context iso # just iso recordings 13 + // node bin/record-corpus.mjs --dry-run # plan only, no API 14 + // node bin/record-corpus.mjs --force # bypass cache 15 + 16 + import { 17 + writeFileSync, readFileSync, mkdirSync, existsSync, 18 + } from "node:fs"; 19 + import { resolve, dirname } from "node:path"; 20 + import { fileURLToPath } from "node:url"; 21 + import { createHash } from "node:crypto"; 22 + 23 + const HERE = dirname(fileURLToPath(import.meta.url)); 24 + const ROOT = resolve(HERE, ".."); 25 + const CORPUS = resolve(ROOT, "corpus"); 26 + const PHONEMES = JSON.parse(readFileSync(resolve(CORPUS, "phonemes.json"), "utf8")); 27 + 28 + const argv = process.argv.slice(2); 29 + const flags = {}; 30 + for (let i = 0; i < argv.length; i++) { 31 + const a = argv[i]; 32 + if (a.startsWith("--")) { 33 + const key = a.slice(2); 34 + const next = argv[i + 1]; 35 + if (next !== undefined && !next.startsWith("--")) { flags[key] = next; i++; } 36 + else flags[key] = true; 37 + } 38 + } 39 + 40 + const ONLY = flags.only ? new Set(String(flags.only).split(",")) : null; 41 + const CONTEXTS = flags.context 42 + ? [String(flags.context)] 43 + : ["iso", "digit", "cvc"]; 44 + const DRY = flags["dry-run"] === true; 45 + const FORCE = flags.force === true; 46 + 47 + const SETTINGS = PHONEMES.settings; 48 + const ENDPOINT = "https://aesthetic.computer/api/say"; 49 + 50 + const manifest = { 51 + version: 1, 52 + generated: new Date().toISOString(), 53 + endpoint: ENDPOINT, 54 + settings: SETTINGS, 55 + items: {}, 56 + }; 57 + 58 + function hashOf(body) { 59 + return createHash("sha256").update(JSON.stringify(body)).digest("hex").slice(0, 16); 60 + } 61 + 62 + function buildBody(text, isIso, phoneme) { 63 + const speed = isIso 64 + ? (phoneme.iso_speed ?? SETTINGS.iso_default_speed) 65 + : SETTINGS.default_speed; 66 + return { 67 + from: text, 68 + provider: SETTINGS.voice.provider, 69 + voice: SETTINGS.voice.voice, 70 + stability: SETTINGS.stability, 71 + similarity: SETTINGS.similarity, 72 + style: SETTINGS.style, 73 + speed, 74 + withTimestamps: true, 75 + }; 76 + } 77 + 78 + async function fetchOne(text, body, outMp3, outAlign) { 79 + const res = await fetch(ENDPOINT, { 80 + method: "POST", 81 + headers: { "Content-Type": "application/json" }, 82 + body: JSON.stringify(body), 83 + redirect: "follow", 84 + }); 85 + if (!res.ok) throw new Error(`/api/say ${res.status}: ${await res.text()}`); 86 + const ct = res.headers.get("content-type") || ""; 87 + if (ct.includes("application/json")) { 88 + // Server returned JSON-with-timestamps. Save audio + alignment. 89 + const json = await res.json(); 90 + const buf = Buffer.from(json.audio, "base64"); 91 + writeFileSync(outMp3, buf); 92 + const a = json.alignment || {}; 93 + const chars = a.characters || []; 94 + const starts = a.character_start_times_seconds || []; 95 + const ends = a.character_end_times_seconds || []; 96 + const words = []; 97 + let i = 0; 98 + while (i < chars.length) { 99 + if (/\s/.test(chars[i])) { i++; continue; } 100 + const wStart = starts[i]; 101 + let txt = ""; 102 + let lastEnd = ends[i]; 103 + while (i < chars.length && !/\s/.test(chars[i])) { 104 + txt += chars[i]; lastEnd = ends[i]; i++; 105 + } 106 + words.push({ text: txt, fromMs: Math.round(wStart * 1000), toMs: Math.round(lastEnd * 1000) }); 107 + } 108 + writeFileSync(outAlign, JSON.stringify({ 109 + source: "elevenlabs/with-timestamps", 110 + text, voice: `${body.provider}/${body.voice}`, 111 + characters: chars, char_starts_s: starts, char_ends_s: ends, words, 112 + }, null, 2)); 113 + return { bytes: buf.length, words: words.length, hasAlignment: true }; 114 + } 115 + // Fallback: server returned raw mp3 (jeffrey-pvc currently doesn't 116 + // support /with-timestamps). Save the audio; alignment can be re-derived 117 + // later via WhisperX if needed. 118 + const buf = Buffer.from(await res.arrayBuffer()); 119 + writeFileSync(outMp3, buf); 120 + writeFileSync(outAlign, JSON.stringify({ 121 + source: "elevenlabs/no-timestamps", 122 + text, voice: `${body.provider}/${body.voice}`, 123 + note: "server returned raw mp3; no character-level alignment available", 124 + }, null, 2)); 125 + return { bytes: buf.length, words: 0, hasAlignment: false }; 126 + } 127 + 128 + let plannedRequests = 0; 129 + let executedRequests = 0; 130 + let cachedHits = 0; 131 + const errors = []; 132 + 133 + for (const phoneme of PHONEMES.phonemes) { 134 + if (ONLY && !ONLY.has(phoneme.id)) continue; 135 + manifest.items[phoneme.id] = { 136 + ipa: phoneme.ipa, 137 + class: phoneme.class, 138 + contexts: {}, 139 + }; 140 + 141 + for (const ctx of CONTEXTS) { 142 + const text = phoneme[ctx]; 143 + if (!text) continue; 144 + plannedRequests++; 145 + const body = buildBody(text, ctx === "iso", phoneme); 146 + const inputHash = hashOf(body); 147 + 148 + const dir = resolve(CORPUS, "raw", ctx); 149 + mkdirSync(dir, { recursive: true }); 150 + const stem = resolve(dir, phoneme.id); 151 + const outMp3 = `${stem}.mp3`; 152 + const outAlign = `${stem}.json`; 153 + const outHash = `${stem}.hash`; 154 + 155 + const cached = 156 + !FORCE && 157 + existsSync(outMp3) && 158 + existsSync(outAlign) && 159 + existsSync(outHash) && 160 + readFileSync(outHash, "utf8").trim() === inputHash; 161 + 162 + manifest.items[phoneme.id].contexts[ctx] = { 163 + text, 164 + hash: inputHash, 165 + mp3: `corpus/raw/${ctx}/${phoneme.id}.mp3`, 166 + alignment: `corpus/raw/${ctx}/${phoneme.id}.json`, 167 + cached, 168 + }; 169 + 170 + if (cached) { 171 + cachedHits++; 172 + continue; 173 + } 174 + if (DRY) { 175 + console.log(` [plan] ${phoneme.id}/${ctx}: ${JSON.stringify(text)}`); 176 + continue; 177 + } 178 + try { 179 + console.log(`→ ${phoneme.id}/${ctx} (${text.length} chars)`); 180 + const r = await fetchOne(text, body, outMp3, outAlign); 181 + writeFileSync(outHash, inputHash + "\n"); 182 + executedRequests++; 183 + console.log(` ✓ ${(r.bytes / 1024).toFixed(0)} KB · ${r.words} words · hash ${inputHash}`); 184 + } catch (err) { 185 + errors.push({ phoneme: phoneme.id, ctx, error: err.message }); 186 + console.error(` ✗ ${err.message}`); 187 + } 188 + } 189 + } 190 + 191 + if (!DRY) { 192 + writeFileSync(resolve(CORPUS, "manifest.json"), JSON.stringify(manifest, null, 2)); 193 + } 194 + 195 + console.log(""); 196 + console.log(`planned: ${plannedRequests}`); 197 + console.log(`executed: ${executedRequests}`); 198 + console.log(`cached: ${cachedHits}`); 199 + if (errors.length) { 200 + console.log(`errors: ${errors.length}`); 201 + for (const e of errors) console.log(` - ${e.phoneme}/${e.ctx}: ${e.error}`); 202 + process.exit(1); 203 + }
+482
pop/voice/bin/render-pt.mjs
··· 1 + #!/usr/bin/env node 2 + // render-pt.mjs — headless Pink Trombone renderer. 3 + // 4 + // Loads vendor/pinktrombone/index.html, extracts Thapen's Glottis + 5 + // Tract object literals + simplex noise lib in-place (no transcription, 6 + // drift-free against upstream), runs them inside a node vm sandbox with 7 + // shimmed globals, and renders parameter trajectories to mono 16-bit 8 + // PCM WAV. The biquad-filtered aspiration/fricative noise inputs are 9 + // approximated as white noise — close enough for vowel quality, will 10 + // matter more once fricative phonemes become a target. 11 + // 12 + // This is what fit.py's render_pt() will eventually call (via subprocess). 13 + // For now it's also a smoke-test driver: render a few vowels to disk. 14 + // 15 + // Usage: 16 + // node bin/render-pt.mjs --vowel ah --duration 1.5 --out ~/Desktop/pt-ah.wav 17 + // node bin/render-pt.mjs --smoke ~/Desktop # render the demo set 18 + // node bin/render-pt.mjs --params '{"tongueIndex":29,"tongueDiameter":2.43,"f0":120}' \ 19 + // --duration 2 --out out.wav 20 + 21 + import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; 22 + import { resolve, dirname } from "node:path"; 23 + import { fileURLToPath } from "node:url"; 24 + import { homedir } from "node:os"; 25 + import vm from "node:vm"; 26 + 27 + const HERE = dirname(fileURLToPath(import.meta.url)); 28 + const ROOT = resolve(HERE, ".."); 29 + const PT_HTML = resolve(ROOT, "vendor/pinktrombone/index.html"); 30 + 31 + // ── Source extraction ───────────────────────────────────────────────── 32 + // PT's HTML carries the entire source inline. We slice out the named 33 + // regions by anchor strings that have been stable since v1.1 (March 34 + // 2017). If any of these slices fail, the upstream's been re-formatted 35 + // and we should re-pin. 36 + function extractRegion(src, startMarker, endMarker, name) { 37 + const start = src.indexOf(startMarker); 38 + if (start < 0) throw new Error(`extract '${name}': missing start marker ${JSON.stringify(startMarker)}`); 39 + const end = src.indexOf(endMarker, start); 40 + if (end < 0) throw new Error(`extract '${name}': missing end marker ${JSON.stringify(endMarker)}`); 41 + return src.slice(start, end + endMarker.length); 42 + } 43 + 44 + function loadPT() { 45 + const html = readFileSync(PT_HTML, "utf8"); 46 + 47 + // Math.clamp + Math.moveTowards (two definitions; the second wins, 48 + // which is the four-arg form we need) 49 + const mathHelpers = extractRegion(html, "Math.clamp = function", "Math.gaussian", "math-helpers") 50 + .replace(/Math\.gaussian[\s\S]*$/, ""); 51 + 52 + // Simplex noise IIFE — public domain (Stefan Gustavson) 53 + const simplexNoise = extractRegion(html, "(function(global){", "})(this);", "simplex-noise"); 54 + 55 + // Glottis + Tract literals — match by their stable terminator pattern. 56 + const glottisMatch = html.match(/var Glottis =\s*\{[\s\S]*?\n\}\s*\n\s*\n\s*var Tract/); 57 + if (!glottisMatch) throw new Error("could not extract Glottis block"); 58 + const glottisBlock = glottisMatch[0].replace(/\n\s*var Tract$/, ""); 59 + 60 + const tractMatch = html.match(/var Tract =\s*\{[\s\S]*?\n\};\s*\n/); 61 + if (!tractMatch) throw new Error("could not extract Tract block"); 62 + const tractBlock = tractMatch[0]; 63 + 64 + return { mathHelpers, simplexNoise, glottisBlock, tractBlock }; 65 + } 66 + 67 + // ── Sandbox + boot ──────────────────────────────────────────────────── 68 + function bootSandbox(sampleRate) { 69 + const sandbox = { 70 + Math: Object.create(Math), 71 + Float64Array, 72 + Float32Array, 73 + Array, 74 + Date, 75 + console, 76 + sampleRate, 77 + alwaysVoice: false, 78 + autoWobble: false, 79 + backCtx: makeNullCtx(), 80 + UI: { touchesWithMouse: [] }, 81 + AudioSystem: { blockLength: 512, blockTime: 512 / sampleRate }, 82 + palePink: "#FFEEF5", 83 + time: 0, 84 + noise: null, 85 + }; 86 + sandbox.global = sandbox; 87 + sandbox.window = sandbox; 88 + sandbox.this = sandbox; 89 + vm.createContext(sandbox); 90 + 91 + const { mathHelpers, simplexNoise, glottisBlock, tractBlock } = loadPT(); 92 + 93 + // Run Math helpers (extends sandbox.Math.clamp, Math.moveTowards) 94 + vm.runInContext(mathHelpers, sandbox); 95 + // Run simplex noise — installs `noise` on the global (the IIFE writes 96 + // global.noise = {…}). Our sandbox is the global, so this works. 97 + vm.runInContext(simplexNoise.replace("})(this);", "})(this);\nthis.noise = noise;"), sandbox); 98 + // Glottis + Tract literals 99 + vm.runInContext(glottisBlock + ";\n" + tractBlock, sandbox); 100 + 101 + // Skip Glottis.init (it draws the keyboard); init waveform manually 102 + sandbox.Glottis.setupWaveform(0); 103 + sandbox.Tract.init(); 104 + return sandbox; 105 + } 106 + 107 + function makeNullCtx() { 108 + const noop = () => {}; 109 + return new Proxy({}, { get: () => noop, set: () => true }); 110 + } 111 + 112 + // ── Parameter shape (the contract fit.py optimizes over) ────────────── 113 + // All values in PT's native units. The fitter passes a flat dict; it 114 + // can override any subset, the rest fall back to the per-pose defaults. 115 + // tongueIndex [12, 29] front-back tongue position 116 + // tongueDiameter [2.05, 3.5] close-open tongue height 117 + // lipMul [0, 1.0] lip aperture multiplier (0=closed, 1=open) 118 + // velumOpen [0.01, 0.4] nasal port (0.01=closed, 0.4=hum) 119 + // f0 Hz fundamental 120 + // tenseness [0, 1] glottal tension 121 + // loudness [0, 1] 122 + // 123 + // `pose` (optional) names a vowel preset whose values fill any unset 124 + // fields. Useful for relative perturbations during fitting. 125 + 126 + // ── Word recipes — keyframe arrays for short transitions ────────────── 127 + // `t` is normalized [0, 1] across the render duration. PT's 128 + // Tract.movementSpeed (15 cm/s) handles fine-grained smoothing inside 129 + // the gap between any two keyframes for free. 130 + // 131 + // These are hand-crafted from the canonical IPA pose values. The fitter 132 + // can refine them against jeffrey-pvc word recordings later — this is 133 + // just the "sounds plausible from the phonetics" baseline. 134 + const WORD_RECIPES = { 135 + // /aɪ/ "eye" — back-open → front-close glide, mostly voiced 136 + eye: [ 137 + { t: 0.0, pose: "ah", f0: 130, tenseness: 0.65 }, 138 + { t: 0.55, pose: "ah", f0: 130, tenseness: 0.65 }, // hold the start 139 + { t: 1.0, pose: "iy", f0: 145, tenseness: 0.60 }, 140 + ], 141 + // /oʊ/ "owe" — back-open-rounded → back-close-rounded 142 + owe: [ 143 + { t: 0.0, pose: "aa", f0: 125, tenseness: 0.65, lipMulOverride: 0.65 }, 144 + { t: 0.5, pose: "aa", f0: 125, tenseness: 0.65, lipMulOverride: 0.55 }, 145 + { t: 1.0, pose: "uw", f0: 130, tenseness: 0.60 }, 146 + ], 147 + // /eɪ/ "ay" — mid-front → close-front 148 + ay: [ 149 + { t: 0.0, tongueIndex: 14.5, tongueDiameter: 2.95, lipMul: 1.0, velumOpen: 0.01, f0: 130, tenseness: 0.6 }, 150 + { t: 0.5, tongueIndex: 14.5, tongueDiameter: 2.95, lipMul: 1.0, velumOpen: 0.01, f0: 130, tenseness: 0.6 }, 151 + { t: 1.0, pose: "iy", f0: 140, tenseness: 0.6 }, 152 + ], 153 + // /wi/ "we" — back-close-rounded glide → front-close 154 + we: [ 155 + { t: 0.0, pose: "uw", f0: 130, tenseness: 0.55 }, 156 + { t: 0.25, pose: "uw", f0: 132, tenseness: 0.6 }, 157 + { t: 1.0, pose: "iy", f0: 145, tenseness: 0.6 }, 158 + ], 159 + // /aʊ/ "ow!" — back-open → back-close-rounded 160 + ow: [ 161 + { t: 0.0, pose: "ah", f0: 130, tenseness: 0.7 }, 162 + { t: 0.5, pose: "ah", f0: 130, tenseness: 0.7 }, 163 + { t: 1.0, pose: "uw", f0: 130, tenseness: 0.55 }, 164 + ], 165 + }; 166 + 167 + // ── Vowel poses (defaults) ──────────────────────────────────────────── 168 + const VOWEL_POSES = { 169 + // front-close 170 + iy: { tongueIndex: 12.9, tongueDiameter: 2.43, velumOpen: 0.01, lipsRound: false }, 171 + // front-open 172 + ae: { tongueIndex: 14, tongueDiameter: 3.4, velumOpen: 0.01, lipsRound: false }, 173 + // open central 174 + ah: { tongueIndex: 22.0, tongueDiameter: 3.5, velumOpen: 0.01, lipsRound: false }, 175 + // back-open 176 + aa: { tongueIndex: 25.0, tongueDiameter: 3.5, velumOpen: 0.01, lipsRound: false }, 177 + // back-close, rounded 178 + uw: { tongueIndex: 27.0, tongueDiameter: 2.3, velumOpen: 0.01, lipsRound: true }, 179 + // schwa — neutral 180 + schwa: { tongueIndex: 17.0, tongueDiameter: 2.8, velumOpen: 0.01, lipsRound: false }, 181 + // nasal hum (closed lips, velum open) 182 + m: { tongueIndex: 17.0, tongueDiameter: 2.8, velumOpen: 0.4, lipsRound: false, lipsClosed: true }, 183 + }; 184 + 185 + // Apply tract pose into Tract.targetDiameter / restDiameter using PT's 186 + // own setRestDiameter math (transcribed from TractUI line 1541). 187 + // `params` is a fully-resolved object (no pose lookup here). 188 + function applyTractParams(s, params) { 189 + const Tract = s.Tract; 190 + const tongueIndex = params.tongueIndex; 191 + const tongueDiameter = params.tongueDiameter; 192 + const lipMul = params.lipMul; 193 + const gridOffset = 1.7; 194 + for (let i = Tract.bladeStart; i < Tract.lipStart; i++) { 195 + const t = 1.1 * Math.PI * (tongueIndex - i) / (Tract.tipStart - Tract.bladeStart); 196 + const fixedTongueDiameter = 2 + (tongueDiameter - 2) / 1.5; 197 + let curve = (1.5 - fixedTongueDiameter + gridOffset) * Math.cos(t); 198 + if (i == Tract.bladeStart - 2 || i == Tract.lipStart - 1) curve *= 0.8; 199 + if (i == Tract.bladeStart || i == Tract.lipStart - 2) curve *= 0.94; 200 + Tract.restDiameter[i] = 1.5 - curve; 201 + } 202 + // Lip aperture region (PT segments lipStart..n-1) 203 + for (let i = Tract.lipStart; i < Tract.n; i++) { 204 + Tract.restDiameter[i] = 1.5 * lipMul; 205 + } 206 + for (let i = 0; i < Tract.n; i++) Tract.targetDiameter[i] = Tract.restDiameter[i]; 207 + Tract.velumTarget = params.velumOpen; 208 + } 209 + 210 + function resolveParams(input) { 211 + // Resolve a partial input against an optional pose preset. 212 + let base; 213 + if (input.pose && VOWEL_POSES[input.pose]) { 214 + const p = VOWEL_POSES[input.pose]; 215 + base = { 216 + tongueIndex: p.tongueIndex, 217 + tongueDiameter: p.tongueDiameter, 218 + lipMul: p.lipsClosed ? 0.0 : (p.lipsRound ? 0.6 : 1.0), 219 + velumOpen: p.velumOpen ?? 0.01, 220 + f0: 120, 221 + tenseness: 0.6, 222 + loudness: 0.8, 223 + }; 224 + } else { 225 + // Schwa-ish defaults if no pose given. 226 + base = { 227 + tongueIndex: 17, tongueDiameter: 2.8, lipMul: 1.0, velumOpen: 0.01, 228 + f0: 120, tenseness: 0.6, loudness: 0.8, 229 + }; 230 + } 231 + return { ...base, ...input }; 232 + } 233 + 234 + // Resolve a single keyframe entry's pose-or-explicit-params shape. 235 + function resolveKeyframe(kf) { 236 + const r = resolveParams(kf); 237 + if (kf.lipMulOverride !== undefined) r.lipMul = kf.lipMulOverride; 238 + return { t: kf.t, params: r }; 239 + } 240 + 241 + // Linear interpolation between two resolved param dicts. 242 + function lerpParams(a, b, alpha) { 243 + const out = {}; 244 + const keys = ["tongueIndex", "tongueDiameter", "lipMul", "velumOpen", 245 + "f0", "tenseness", "loudness"]; 246 + for (const k of keys) out[k] = a[k] * (1 - alpha) + b[k] * alpha; 247 + return out; 248 + } 249 + 250 + // Find the params at a given normalized time t ∈ [0, 1] from a sorted 251 + // keyframe array, by piecewise linear interpolation. 252 + function paramsAtT(keyframes, t) { 253 + if (t <= keyframes[0].t) return keyframes[0].params; 254 + if (t >= keyframes[keyframes.length - 1].t) return keyframes[keyframes.length - 1].params; 255 + for (let i = 0; i < keyframes.length - 1; i++) { 256 + const a = keyframes[i], b = keyframes[i + 1]; 257 + if (t >= a.t && t <= b.t) { 258 + const span = (b.t - a.t) || 1; 259 + const alpha = (t - a.t) / span; 260 + return lerpParams(a.params, b.params, alpha); 261 + } 262 + } 263 + return keyframes[keyframes.length - 1].params; 264 + } 265 + 266 + // ── Render ───────────────────────────────────────────────────────────── 267 + function render(input = {}) { 268 + const sampleRate = input.sampleRate ?? 22050; 269 + const durationS = input.durationS ?? 1.5; 270 + const voiced = input.voiced ?? true; 271 + const rampInS = input.rampInS ?? 0.05; 272 + const rampOutS = input.rampOutS ?? 0.1; 273 + const seed = input.seed ?? 1; 274 + 275 + // Resolve keyframes if provided; otherwise treat input as a single 276 + // steady-state pose (back-compat with v0.1 callers). 277 + let keyframes = null; 278 + if (input.keyframes && Array.isArray(input.keyframes) && input.keyframes.length > 0) { 279 + keyframes = input.keyframes 280 + .map(resolveKeyframe) 281 + .sort((x, y) => x.t - y.t); 282 + } 283 + const initialParams = keyframes ? keyframes[0].params : resolveParams(input); 284 + 285 + const s = bootSandbox(sampleRate); 286 + applyTractParams(s, initialParams); 287 + 288 + // Glottis driving values (UI* are the inputs PT reads each block) 289 + s.Glottis.UIFrequency = initialParams.f0; 290 + s.Glottis.UITenseness = initialParams.tenseness; 291 + s.Glottis.smoothFrequency = initialParams.f0; 292 + s.Glottis.loudness = initialParams.loudness; 293 + s.alwaysVoice = voiced; 294 + s.autoWobble = false; // determinism for tests; fitter can flip on 295 + s.noise.seed(seed); 296 + 297 + const N = Math.round(durationS * sampleRate); 298 + const out = new Float32Array(N); 299 + const blockLen = s.AudioSystem.blockLength; 300 + const rampInN = Math.round(rampInS * sampleRate); 301 + const rampOutStart = N - Math.round(rampOutS * sampleRate); 302 + 303 + // Pre-allocate noise buffer for a block. 304 + const aspNoise = new Float32Array(blockLen); 305 + const fricNoise = new Float32Array(blockLen); 306 + 307 + for (let blockStart = 0; blockStart < N; blockStart += blockLen) { 308 + // Inject keyframe-interpolated params for this block (if a 309 + // trajectory was given). PT's tract.movementSpeed adds another 310 + // layer of smoothing on top of these per-block targets. 311 + if (keyframes) { 312 + const tNorm = blockStart / Math.max(1, N - 1); 313 + const p = paramsAtT(keyframes, tNorm); 314 + applyTractParams(s, p); 315 + s.Glottis.UIFrequency = p.f0; 316 + s.Glottis.UITenseness = p.tenseness; 317 + s.Glottis.loudness = p.loudness; 318 + } 319 + // Fill noise inputs (white noise; biquad filtering is a v0.2 feature). 320 + for (let j = 0; j < blockLen; j++) { 321 + aspNoise[j] = Math.random() * 2 - 1; 322 + fricNoise[j] = Math.random() * 2 - 1; 323 + } 324 + const blockN = Math.min(blockLen, N - blockStart); 325 + for (let j = 0; j < blockN; j++) { 326 + const lambda1 = j / blockLen; 327 + const lambda2 = (j + 0.5) / blockLen; 328 + const glottalOutput = s.Glottis.runStep(lambda1, aspNoise[j]); 329 + let vocal = 0; 330 + s.Tract.runStep(glottalOutput, fricNoise[j], lambda1); 331 + vocal += s.Tract.lipOutput + s.Tract.noseOutput; 332 + s.Tract.runStep(glottalOutput, fricNoise[j], lambda2); 333 + vocal += s.Tract.lipOutput + s.Tract.noseOutput; 334 + let sample = vocal * 0.125; 335 + // soft attack/release (PT's intensity ramp handles most of this, 336 + // but a hard tail-clip on stop avoids the residual tract ringing 337 + // bleeding into a fade) 338 + const i = blockStart + j; 339 + if (i < rampInN) sample *= i / rampInN; 340 + else if (i >= rampOutStart) sample *= Math.max(0, (N - i) / (N - rampOutStart)); 341 + out[i] = sample; 342 + } 343 + s.Glottis.finishBlock(); 344 + s.Tract.finishBlock(); 345 + } 346 + return { audio: out, sampleRate }; 347 + } 348 + 349 + // ── WAV encoder (16-bit PCM mono) ───────────────────────────────────── 350 + function encodeWav(float32, sampleRate) { 351 + const N = float32.length; 352 + const buf = Buffer.alloc(44 + N * 2); 353 + // RIFF header 354 + buf.write("RIFF", 0); 355 + buf.writeUInt32LE(36 + N * 2, 4); 356 + buf.write("WAVE", 8); 357 + buf.write("fmt ", 12); 358 + buf.writeUInt32LE(16, 16); 359 + buf.writeUInt16LE(1, 20); // PCM 360 + buf.writeUInt16LE(1, 22); // mono 361 + buf.writeUInt32LE(sampleRate, 24); 362 + buf.writeUInt32LE(sampleRate * 2, 28); 363 + buf.writeUInt16LE(2, 32); 364 + buf.writeUInt16LE(16, 34); 365 + buf.write("data", 36); 366 + buf.writeUInt32LE(N * 2, 40); 367 + // Find peak for safe normalization (target -3 dBFS). 368 + let peak = 1e-9; 369 + for (let i = 0; i < N; i++) peak = Math.max(peak, Math.abs(float32[i])); 370 + const gain = 0.7079 / peak; // -3 dBFS 371 + for (let i = 0; i < N; i++) { 372 + const v = Math.max(-1, Math.min(1, float32[i] * gain)); 373 + buf.writeInt16LE(Math.round(v * 32767), 44 + i * 2); 374 + } 375 + return buf; 376 + } 377 + 378 + function expandHome(p) { 379 + if (!p) return p; 380 + if (p === "~") return homedir(); 381 + if (p.startsWith("~/")) return resolve(homedir(), p.slice(2)); 382 + return p; 383 + } 384 + 385 + // ── CLI ─────────────────────────────────────────────────────────────── 386 + async function main() { 387 + const argv = process.argv.slice(2); 388 + const flags = {}; 389 + for (let i = 0; i < argv.length; i++) { 390 + const a = argv[i]; 391 + if (a.startsWith("--")) { 392 + const key = a.slice(2); 393 + const next = argv[i + 1]; 394 + if (next !== undefined && !next.startsWith("--")) { flags[key] = next; i++; } 395 + else flags[key] = true; 396 + } 397 + } 398 + 399 + if (flags.smoke) { 400 + const dest = expandHome(typeof flags.smoke === "string" ? flags.smoke : `${homedir()}/Desktop`); 401 + mkdirSync(dest, { recursive: true }); 402 + const set = [ 403 + { name: "pt-ah", pose: "ah", f0: 120, tenseness: 0.6, durationS: 1.5 }, 404 + { name: "pt-ee", pose: "iy", f0: 120, tenseness: 0.6, durationS: 1.5 }, 405 + { name: "pt-oo", pose: "uw", f0: 120, tenseness: 0.6, durationS: 1.5 }, 406 + { name: "pt-aa", pose: "aa", f0: 120, tenseness: 0.65, durationS: 1.5 }, 407 + { name: "pt-schwa", pose: "schwa", f0: 110, tenseness: 0.55, durationS: 1.5 }, 408 + { name: "pt-hum", pose: "m", f0: 110, tenseness: 0.55, durationS: 1.5 }, 409 + ]; 410 + console.log(`→ rendering smoke set into ${dest}`); 411 + for (const item of set) { 412 + const t0 = Date.now(); 413 + const { audio, sampleRate } = render(item); 414 + const wav = encodeWav(audio, sampleRate); 415 + const out = resolve(dest, `${item.name}.wav`); 416 + writeFileSync(out, wav); 417 + console.log(` ✓ ${item.name}.wav (${(wav.length/1024).toFixed(0)} KB · ${item.durationS}s · ${Date.now()-t0}ms)`); 418 + } 419 + console.log("\ndone. open ~/Desktop and double-click to listen."); 420 + return; 421 + } 422 + 423 + if (flags.words) { 424 + // Render every word recipe to ~/Desktop. Optional override of duration. 425 + const dest = expandHome(typeof flags.words === "string" ? flags.words : `${homedir()}/Desktop`); 426 + const durationS = Number(flags.duration ?? 1.0); 427 + mkdirSync(dest, { recursive: true }); 428 + console.log(`→ rendering words into ${dest} (${durationS}s each)`); 429 + for (const [name, keyframes] of Object.entries(WORD_RECIPES)) { 430 + const t0 = Date.now(); 431 + const { audio, sampleRate } = render({ keyframes, durationS }); 432 + const wav = encodeWav(audio, sampleRate); 433 + const out = resolve(dest, `pt-word-${name}.wav`); 434 + writeFileSync(out, wav); 435 + console.log(` ✓ pt-word-${name}.wav (${(wav.length/1024).toFixed(0)} KB · ${Date.now()-t0}ms · ${keyframes.length} keyframes)`); 436 + } 437 + return; 438 + } 439 + 440 + // Single render 441 + let input; 442 + if (flags.word) { 443 + const recipe = WORD_RECIPES[String(flags.word)]; 444 + if (!recipe) { 445 + console.error(`✗ unknown word: ${flags.word}. options: ${Object.keys(WORD_RECIPES).join(", ")}`); 446 + process.exit(1); 447 + } 448 + input = { keyframes: recipe }; 449 + } else if (flags.params) { 450 + input = JSON.parse(flags.params); 451 + } else if (flags.keyframes) { 452 + input = { keyframes: JSON.parse(flags.keyframes) }; 453 + } else { 454 + input = { pose: flags.vowel || flags.pose || "schwa" }; 455 + } 456 + if (flags.duration) input.durationS = Number(flags.duration); 457 + if (flags.f0) input.f0 = Number(flags.f0); 458 + if (flags.tenseness) input.tenseness = Number(flags.tenseness); 459 + if (flags["sample-rate"]) input.sampleRate = Number(flags["sample-rate"]); 460 + if (flags.seed) input.seed = Number(flags.seed); 461 + 462 + const out = expandHome(flags.out 463 + || `~/Desktop/pt-${flags.word || input.pose || "params"}.wav`); 464 + const isStdout = out === "-"; 465 + const quiet = flags.quiet === true || isStdout; 466 + if (!quiet) { 467 + const summary = JSON.stringify({ ...input, durationS: input.durationS ?? 1.5 }); 468 + console.log(`→ ${summary} → ${out}`); 469 + } 470 + const { audio, sampleRate } = render(input); 471 + const wav = encodeWav(audio, sampleRate); 472 + if (isStdout) { 473 + // raw WAV bytes — used by fit.py subprocess 474 + process.stdout.write(wav); 475 + } else { 476 + mkdirSync(dirname(out), { recursive: true }); 477 + writeFileSync(out, wav); 478 + if (!quiet) console.log(`✓ ${out} (${(wav.length/1024).toFixed(0)} KB)`); 479 + } 480 + } 481 + 482 + main().catch((e) => { console.error(e); process.exit(1); });
+98
pop/voice/bin/vendor-pt.sh
··· 1 + #!/usr/bin/env bash 2 + # vendor-pt.sh — fetch Neil Thapen's original Pink Trombone (MIT) into 3 + # vendor/pinktrombone/. 4 + # 5 + # PT lives canonically at https://dood.al/pinktrombone/ as a single 6 + # self-contained HTML file. The MIT license notice is the comment header 7 + # at the top of that file (lines 28–46 as of v1.1, March 2017). 8 + # 9 + # We fetch the HTML directly with curl rather than cloning a third-party 10 + # git mirror, because: 11 + # 1. dood.al IS the canonical source — every github mirror is downstream 12 + # 2. some mirrors (zakaton/Pink-Trombone, others) re-license under GPL 13 + # after refactoring; that's incompatible with the AC-native goal of 14 + # shipping a permissive C/WASM jeffrey voice 15 + # 3. a single HTML file pinned by sha256 is the simplest possible 16 + # provenance contract 17 + # 18 + # Usage: 19 + # bin/vendor-pt.sh # fetch from dood.al 20 + # bin/vendor-pt.sh <url> # override source 21 + # bin/vendor-pt.sh --dry-run # print what it would do, no fetch 22 + 23 + set -euo pipefail 24 + 25 + HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 26 + ROOT="$(cd "$HERE/.." && pwd)" 27 + VENDOR_DIR="$ROOT/vendor/pinktrombone" 28 + 29 + DEFAULT_URL="https://dood.al/pinktrombone/" 30 + 31 + URL="${1:-$DEFAULT_URL}" 32 + DRY=0 33 + [ "${1:-}" = "--dry-run" ] && DRY=1 && URL="$DEFAULT_URL" 34 + 35 + echo "→ vendoring pink trombone (MIT, Neil Thapen 2017)" 36 + echo " url: $URL" 37 + echo " target: $VENDOR_DIR" 38 + 39 + if [ -f "$VENDOR_DIR/index.html" ] && [ -f "$VENDOR_DIR/UPSTREAM.txt" ]; then 40 + echo "✗ $VENDOR_DIR already populated. delete it first if you want a fresh fetch." 41 + exit 1 42 + fi 43 + 44 + if [ "$DRY" = 1 ]; then 45 + echo " (dry run — would curl now)" 46 + exit 0 47 + fi 48 + 49 + mkdir -p "$VENDOR_DIR" 50 + 51 + TMP="$(mktemp)" 52 + trap 'rm -f "$TMP"' EXIT 53 + curl -sSLf "$URL" -o "$TMP" 54 + 55 + # Sanity-check: confirm the MIT notice we expect is in the file. Refuse 56 + # to vendor an unverified blob. 57 + if ! grep -q "Copyright 2017 Neil Thapen" "$TMP"; then 58 + echo "✗ fetched file missing 'Copyright 2017 Neil Thapen' header — wrong upstream?" 59 + exit 1 60 + fi 61 + if ! grep -q "Permission is hereby granted, free of charge" "$TMP"; then 62 + echo "✗ fetched file missing MIT preamble — license check failed." 63 + exit 1 64 + fi 65 + 66 + cp "$TMP" "$VENDOR_DIR/index.html" 67 + 68 + # Extract the MIT block into a stand-alone LICENSE file so anyone reading 69 + # the vendor tree sees the license without parsing HTML comments. 70 + awk ' 71 + /Copyright 2017 Neil Thapen/ { writing=1 } 72 + writing { print } 73 + /IN THE SOFTWARE\./ { if (writing) { writing=0; exit } } 74 + ' "$TMP" > "$VENDOR_DIR/LICENSE" 75 + 76 + SHA="$(shasum -a 256 "$VENDOR_DIR/index.html" | awk '{print $1}')" 77 + SIZE="$(wc -c < "$VENDOR_DIR/index.html" | tr -d ' ')" 78 + 79 + cat > "$VENDOR_DIR/UPSTREAM.txt" <<EOF 80 + upstream: $URL 81 + upstream_url: ${URL}index.html 82 + fetched_at: $(date -u +"%Y-%m-%dT%H:%M:%SZ") 83 + sha256: $SHA 84 + size_bytes: $SIZE 85 + version: "version 1.1, March 2017" (per index.html line 8) 86 + author: Neil Thapen <venuspatrol.nfshost.com> 87 + license: MIT (Copyright 2017 Neil Thapen — see LICENSE in this directory) 88 + notice: pop/voice/NOTICE.md tracks third-party material for this lane 89 + EOF 90 + 91 + echo "✓ vendored pink trombone" 92 + echo " upstream: $URL" 93 + echo " sha256: $SHA" 94 + echo " size: $SIZE bytes" 95 + echo " files: index.html, LICENSE, UPSTREAM.txt" 96 + echo 97 + echo "next: read script/PinkTrombone.js logic from index.html lines 50+," 98 + echo " then write bin/render-pt.mjs (node-side renderer)."
+62
pop/voice/corpus/phonemes.json
··· 1 + { 2 + "$schema": "./phonemes.schema.json", 3 + "version": 1, 4 + "generated": "2026-05-04", 5 + "note": "Machine-readable phoneme target list for the jeffrey harness. See pop/voice/CORPUS.md for the design rationale. Each entry has an IPA symbol, an arpabet alias for tools that don't speak IPA, three carrier prompts (iso/digit/cvc), and a 'class' tag for analysis grouping. The 'iso_speed' field is per-entry because diphthongs sustain awkwardly at 0.85 — they need 1.0 to keep the glide audible.", 6 + "settings": { 7 + "voice": { "provider": "jeffrey", "voice": "neutral:0" }, 8 + "stability": 0.5, 9 + "similarity": 0.9, 10 + "style": 0.0, 11 + "default_speed": 1.0, 12 + "iso_default_speed": 0.85 13 + }, 14 + "phonemes": [ 15 + { "id": "ae", "ipa": "æ", "arpa": "AE", "class": "short_vowel", "iso": "aaaaa as in cat", "digit": "the cat sat at bat", "cvc": "bat" }, 16 + { "id": "eh", "ipa": "ɛ", "arpa": "EH", "class": "short_vowel", "iso": "eeeee as in bet", "digit": "let met set bet", "cvc": "bet" }, 17 + { "id": "ih", "ipa": "ɪ", "arpa": "IH", "class": "short_vowel", "iso": "iiiii as in bit", "digit": "hit fit lit bit", "cvc": "bit" }, 18 + { "id": "ah", "ipa": "ɒ", "arpa": "AA", "class": "short_vowel", "iso": "ahhhh as in bot", "digit": "got hot lot bot", "cvc": "bot" }, 19 + { "id": "uh", "ipa": "ʊ", "arpa": "UH", "class": "short_vowel", "iso": "uuuuu as in book", "digit": "look took cook book", "cvc": "book" }, 20 + 21 + { "id": "iy", "ipa": "iː", "arpa": "IY", "class": "long_vowel", "iso": "eeeee as in see", "digit": "see me three be", "cvc": "see" }, 22 + { "id": "uw", "ipa": "uː", "arpa": "UW", "class": "long_vowel", "iso": "ooooo as in soup", "digit": "soup loop you do", "cvc": "soup" }, 23 + { "id": "aa", "ipa": "ɑː", "arpa": "AA2", "class": "long_vowel", "iso": "aaaaa as in spa", "digit": "spa la pa ah", "cvc": "spa" }, 24 + { "id": "ao", "ipa": "ɔː", "arpa": "AO", "class": "long_vowel", "iso": "aw aw aw as in saw", "digit": "saw paw raw thaw", "cvc": "saw" }, 25 + { "id": "er", "ipa": "ɜː", "arpa": "ER", "class": "long_vowel", "iso": "errrr as in surf", "digit": "her per stir surf", "cvc": "surf" }, 26 + 27 + { "id": "ay", "ipa": "aɪ", "arpa": "AY", "class": "diphthong", "iso": "ay ay ay as in eye", "digit": "eye my pie tie", "cvc": "pie", "iso_speed": 1.0 }, 28 + { "id": "aw", "ipa": "aʊ", "arpa": "AW", "class": "diphthong", "iso": "ow ow ow as in owl", "digit": "owl how now cow", "cvc": "cow", "iso_speed": 1.0 }, 29 + { "id": "ey", "ipa": "eɪ", "arpa": "EY", "class": "diphthong", "iso": "ay ay ay as in ate", "digit": "ate late date gate", "cvc": "ate", "iso_speed": 1.0 }, 30 + { "id": "ow", "ipa": "oʊ", "arpa": "OW", "class": "diphthong", "iso": "oh oh oh as in owe", "digit": "owe go so no", "cvc": "owe", "iso_speed": 1.0 }, 31 + { "id": "oy", "ipa": "ɔɪ", "arpa": "OY", "class": "diphthong", "iso": "oy oy oy as in oil", "digit": "oil boy toy joy", "cvc": "oil", "iso_speed": 1.0 }, 32 + 33 + { "id": "v", "ipa": "v", "arpa": "V", "class": "voiced_fricative", "iso": "vvvvv as in van", "digit": "van vine vow vote", "cvc": "van" }, 34 + { "id": "dh", "ipa": "ð", "arpa": "DH", "class": "voiced_fricative", "iso": "this this this", "digit": "this that thus those", "cvc": "this" }, 35 + { "id": "z", "ipa": "z", "arpa": "Z", "class": "voiced_fricative", "iso": "zzzzz as in zoo", "digit": "zoo buzz fizz haze", "cvc": "zoo" }, 36 + { "id": "zh", "ipa": "ʒ", "arpa": "ZH", "class": "voiced_fricative", "iso": "zh zh zh as in vision", "digit": "vision azure beige garage", "cvc": "vision" }, 37 + 38 + { "id": "f", "ipa": "f", "arpa": "F", "class": "unvoiced_fricative", "iso": "fffff as in fan", "digit": "fan fine fee fun", "cvc": "fan" }, 39 + { "id": "th", "ipa": "θ", "arpa": "TH", "class": "unvoiced_fricative", "iso": "thin thin thin", "digit": "thin think thaw thug", "cvc": "thin" }, 40 + { "id": "s", "ipa": "s", "arpa": "S", "class": "unvoiced_fricative", "iso": "sssss as in see", "digit": "see say sip sun", "cvc": "see" }, 41 + { "id": "sh", "ipa": "ʃ", "arpa": "SH", "class": "unvoiced_fricative", "iso": "sh sh sh as in ship", "digit": "ship show sheen shut", "cvc": "ship" }, 42 + 43 + { "id": "b", "ipa": "b", "arpa": "B", "class": "voiced_plosive", "iso": "buh buh buh", "digit": "buy bee bay bow", "cvc": "buy" }, 44 + { "id": "d", "ipa": "d", "arpa": "D", "class": "voiced_plosive", "iso": "duh duh duh", "digit": "do day die dough", "cvc": "do" }, 45 + { "id": "g", "ipa": "g", "arpa": "G", "class": "voiced_plosive", "iso": "guh guh guh", "digit": "go gay guy gum", "cvc": "go" }, 46 + { "id": "p", "ipa": "p", "arpa": "P", "class": "unvoiced_plosive", "iso": "puh puh puh", "digit": "pie pay pee pow", "cvc": "pie" }, 47 + { "id": "t", "ipa": "t", "arpa": "T", "class": "unvoiced_plosive", "iso": "tuh tuh tuh", "digit": "two tea tie toe", "cvc": "two" }, 48 + { "id": "k", "ipa": "k", "arpa": "K", "class": "unvoiced_plosive", "iso": "kuh kuh kuh", "digit": "key cue cow car", "cvc": "key" }, 49 + 50 + { "id": "m", "ipa": "m", "arpa": "M", "class": "nasal", "iso": "mmmmm as in me", "digit": "me my mow moon", "cvc": "me" }, 51 + { "id": "n", "ipa": "n", "arpa": "N", "class": "nasal", "iso": "nnnnn as in no", "digit": "no know new neat", "cvc": "no" }, 52 + { "id": "ng", "ipa": "ŋ", "arpa": "NG", "class": "nasal", "iso": "nggg nggg nggg", "digit": "sing ring king song", "cvc": "sing" }, 53 + 54 + { "id": "l", "ipa": "l", "arpa": "L", "class": "liquid", "iso": "lllll as in lo", "digit": "lo low lay lip", "cvc": "lo" }, 55 + { "id": "r", "ipa": "ɹ", "arpa": "R", "class": "liquid", "iso": "rrrrr as in row", "digit": "row ray rye red", "cvc": "row" }, 56 + 57 + { "id": "w", "ipa": "w", "arpa": "W", "class": "glide", "iso": "wwwww as in we", "digit": "we way why wow", "cvc": "we" }, 58 + { "id": "y", "ipa": "j", "arpa": "Y", "class": "glide", "iso": "yyyyy as in you", "digit": "you yes yay yam", "cvc": "you" }, 59 + 60 + { "id": "schwa", "ipa": "ə", "arpa": "AX", "class": "schwa", "iso": "uhhhh", "digit": "the a but um", "cvc": "the" } 61 + ] 62 + }
+98
pop/voice/diphones/README.md
··· 1 + # diphones — the shippable AC-native jeffrey synth 2 + 3 + The pivot, 2026-05-04: **Pink Trombone is a vocal-tract instrument, not a speech synthesizer.** It can't reach phoneme-level intelligibility from photogrammetric priors + speech samples alone. Confirmed audibly on Desktop's `pt-word-eye-fitted.wav`. 4 + 5 + This lane reframes around a different question: *what would it take to ship a tiny jeffrey-voiced TTS that runs on AC-native hardware?* 6 + 7 + ## Approach: jeffrey-pvc-distilled diphone bank 8 + 9 + Use **ElevenLabs jeffrey-pvc as the teacher.** Generate a corpus of carrier sentences chosen to elicit every English diphone (≈1500 transitions). Forced-align with WhisperX (already in the recap pipeline per memory) to extract each ~50 ms slice. Store as a flat lookup: `diphone[a, b] → 50 ms WAV`. Concat with crossfade at synth time. 10 + 11 + Why this and not Pink Trombone: 12 + - **It IS jeffrey.** Diphones are jeffrey's actual voice in fragments, not a model's approximation 13 + - **Tiny shippable size** — 1500 diphones × 50 ms × 22050 Hz × 2 bytes ≈ 3.3 MB raw, less under Opus 14 + - **No NN at runtime** — pure DSP concat, runs trivially in C/WASM 15 + - **AC-native by construction** — every diphone is URL-addressable: `assets.aesthetic.computer/jeffrey-diphones/<a>_<b>.opus` 16 + - **The recap pipeline already has WhisperX** — we reuse the forced-alignment lane 17 + 18 + Why not (TBD: revisit if diphone bank fails): 19 + - A tiny RNN/conv vocoder distilled from jeffrey-pvc would be more *natural-sounding* across novel words but adds inference cost + training complexity 20 + - HTS-style HMM parametric synth is proven shippable (Festvox lineage) but the training pipeline is involved 21 + 22 + ## Cost estimate 23 + 24 + ARPABet has 39 phonemes + start/end markers ≈ 41². That's 1681 ordered pairs but most are illegal (English doesn't have /ŋ_t/ word-initial, etc.). Realistic English diphone count: **~1500**. 25 + 26 + Each carrier sentence carries ~10–15 diphones. 1500 / 12 ≈ **~125 carrier sentences**, each ~5 words long. ElevenLabs charges ~30k chars/$5 of credits at jeffrey-pvc rates, so: 27 + 28 + - 125 carriers × ~25 chars = ~3125 chars 29 + - That's ~$0.50 of credits 30 + - Plus regenerations, alignment passes, retries → call it ~**$2-5 in ElevenLabs** 31 + 32 + This is the cost gate. Negligible compared to a real corpus. 33 + 34 + ## Pipeline 35 + 36 + ``` 37 + diphone-targets.json (the master list of 1500 ARPABet pairs) 38 + 39 + build-carriers.py (assigns pairs to 125 carrier sentences) 40 + 41 + carriers.json (sentence text + which diphones it carries) 42 + 43 + record-carriers.mjs (jeffrey-pvc /api/say with timestamps) 44 + 45 + carriers/raw/*.mp3 (~125 mp3s) 46 + 47 + align-carriers.py (WhisperX forced alignment per phoneme) 48 + 49 + diphone-extraction.py (slice carriers into diphones, ~50 ms each, crossfade-ready) 50 + 51 + diphones/<a>_<b>.opus (~1500 tiny files) 52 + 53 + synth.{c,mjs,wasm} (concat + crossfade at synth time) 54 + ``` 55 + 56 + Every step caches by content hash. Reruns cost $0 unless inputs change. 57 + 58 + ## File map (planned) 59 + 60 + ``` 61 + diphones/ 62 + README.md this file 63 + diphone-targets.json 1500 ARPABet pairs + frequency weight 64 + carriers.json carrier sentences + diphone mapping 65 + bin/ 66 + build-carriers.py pair → sentence packing 67 + record-carriers.mjs /api/say recorder for carriers 68 + align-carriers.py WhisperX forced alignment 69 + extract-diphones.py slice carriers into diphone WAVs 70 + synth.mjs reference TTS implementation 71 + raw/ carrier mp3s (gitignored) 72 + aligned/ WhisperX outputs (gitignored) 73 + bank/ <a>_<b>.opus diphone files (gitignored; published to CDN) 74 + manifest.json phoneme → URL/path map (committed) 75 + ``` 76 + 77 + ## Status 78 + 79 + - 2026-05-04 — lane scaffolded. 80 + - 2026-05-04 — `diphone-targets.json` committed: 1527 ARPABet pairs, 532 in tier-1 (vowel-vowel + CV/VC, the load-bearing perceptual ones). 81 + - 2026-05-04 — `carriers.json` committed (v2 paragraph-form, 10 paragraphs, 2326 chars, ~$0.39 ElevenLabs). `raw/p000..p009.mp3` recorded. 82 + - 2026-05-04 — `align-paragraphs.py` working. torchaudio.pipelines.MMS_FA + CTC forced_align + g2p_en for ARPABet. 1510 phonemes aligned across 168.7s of audio. 83 + - 2026-05-04 — `extract-diphones.py` working. 485 unique diphones in `bank/<a>_<b>.wav`, 3.2 MB total. `manifest.json` committed. 84 + - 2026-05-04 — `synth-word.py` working. Concat-synth proof on 10 words → `~/Desktop/voice-lab/jeff-synth/`. 85 + - known gap: word-boundary diphones (`#B_X`, `X_#E`) systematically missing. Paragraphs join words without silence in alignment, so no boundary phonemes get tagged. Fix in next pass: insert silence padding at word boundaries during extraction, or fallback to half-slices. 86 + - coverage: 175/532 tier-1 (33%), 480/1527 all-tier (31%). To hit ~90% tier-1, add ~30 more paragraph carriers (~$1.20 spend). 87 + - next: listen-test the synth; if quality is there, expand carriers + close boundary gap; if not, swap to a true phoneme-output wav2vec2 model and re-align. 88 + 89 + ## Predecessors 90 + 91 + - **Festvox / Festival** — diphone-based concatenative synthesis, the OG approach (Black, Taylor, Caley 1998+). Most "voice cloning" tools today are NN-based; diphone is a deliberate choice for shippability. 92 + - **MBROLA** — diphone synthesis runtime, lots of language-specific voices, GPL. 93 + - **eSpeak NG** — formant-based, lower quality but tiny (~1MB binary). Inspirational for size, not for jeffrey-fidelity. 94 + - **AC-native formant synth** (dropped 2026-05-03 per memory) — the "sounded like tones" attempt. Diphone bank is the structural fix because jeffrey's actual voice replaces the tone generator. 95 + 96 + ## Why not the PT lane (predecessor in this same `pop/voice/` dir) 97 + 98 + `pop/voice/PHYSIOLOGY.md` and `MODEL-EXTENSIONS.md` document a complementary research path: anatomically-grounded Pink Trombone fits. Those measurements are still valuable — they bound jeffrey's vocal-tract length, lip aperture, etc., which inform what diphone *durations* and *pitch ranges* are realistic for him. But PT itself is reframed: **timbral instrument for big-pictures musical layers, not the speech engine.**
+277
pop/voice/diphones/bin/align-paragraphs.py
··· 1 + #!/usr/bin/env python3 2 + """align-paragraphs.py — phoneme-level forced alignment for the 3 + diphone-bank carrier paragraphs. 4 + 5 + Pipeline per carrier: 6 + 1. Load mp3 → 16 kHz mono float 7 + 2. Run torchaudio.pipelines.MMS_FA acoustic model → per-frame logits 8 + 3. Tokenize the carrier transcript into characters (the MMS_FA vocab) 9 + 4. CTC forced alignment via torchaudio.functional.forced_align 10 + 5. Expand char-level spans to word-level (split by " ") 11 + 6. For each word, run g2p_en → ARPABet sequence, distribute the 12 + word's time uniformly across phonemes 13 + 7. Write aligned/<id>.json with {words: [...], phonemes: [...]} 14 + 15 + Limitations (acknowledged): 16 + * Phoneme-time distribution within a word is uniform, not acoustic. 17 + Diphone extraction with ~50 ms slices + crossfade tolerates this. 18 + * g2p_en approximates OOV words via a neural seq2seq fallback. Not 19 + perfect for AC neologisms (e.g. "kidlisp" came out missing /P/). 20 + A manual override dict for known AC vocabulary is in this file. 21 + * MMS_FA was trained on broad multilingual data; English-only 22 + character set is used here. Punctuation is stripped. 23 + 24 + Usage: 25 + python bin/align-paragraphs.py 26 + python bin/align-paragraphs.py --only p000,p001 27 + """ 28 + from __future__ import annotations 29 + 30 + import argparse 31 + import json 32 + import re 33 + import sys 34 + import unicodedata 35 + from pathlib import Path 36 + 37 + import librosa # type: ignore 38 + import numpy as np 39 + import torch # type: ignore 40 + import torchaudio # type: ignore 41 + import torchaudio.functional as F # type: ignore 42 + from g2p_en import G2p # type: ignore 43 + 44 + HERE = Path(__file__).resolve().parent 45 + ROOT = HERE.parent # pop/voice/diphones/ 46 + RAW = ROOT / "raw" 47 + ALIGNED = ROOT / "aligned" 48 + ALIGNED.mkdir(exist_ok=True) 49 + 50 + # AC neologism overrides — g2p_en's neural fallback is unreliable for 51 + # these. Keys are lowercase whole words; values are ARPABet phonemes 52 + # (no stress markers — alignment doesn't use them). 53 + AC_VOCAB = { 54 + "kidlisp": ["K", "IH", "D", "L", "IH", "S", "P"], 55 + "notepat": ["N", "OW", "T", "P", "AE", "T"], 56 + "fedac": ["F", "EH", "D", "AE", "K"], 57 + "plork": ["P", "L", "AO", "R", "K"], 58 + "whistlegraph": ["W", "IH", "S", "AH", "L", "G", "R", "AE", "F"], 59 + "nopaint": ["N", "OW", "P", "EY", "N", "T"], 60 + "sotce": ["S", "AH", "T", "S", "IY"], 61 + "honeydo": ["HH", "AH", "N", "IY", "D", "UW"], 62 + "lacma": ["L", "AE", "K", "M", "AH"], 63 + "aesthetic": ["EH", "S", "TH", "EH", "T", "IH", "K"], 64 + "fia": ["F", "IY", "AH"], 65 + "menuband": ["M", "EH", "N", "Y", "UW", "B", "AE", "N", "D"], 66 + "geckos": ["G", "EH", "K", "OW", "Z"], 67 + "jamsocket": ["JH", "AE", "M", "S", "AA", "K", "AH", "T"], 68 + } 69 + 70 + 71 + def normalize_text(s: str) -> str: 72 + """Lowercase, strip punctuation, collapse whitespace.""" 73 + s = unicodedata.normalize("NFKD", s) 74 + s = s.lower() 75 + # Replace em-dash and other separators with space 76 + s = re.sub(r"[—–\-]", " ", s) 77 + # Drop everything that's not a-z, space, or apostrophe 78 + s = re.sub(r"[^a-z' ]", " ", s) 79 + s = re.sub(r"\s+", " ", s).strip() 80 + return s 81 + 82 + 83 + def words_to_phonemes(words: list[str], g2p: G2p) -> list[list[str]]: 84 + """Per-word ARPABet phoneme list. Strip stress markers.""" 85 + out = [] 86 + for w in words: 87 + if w in AC_VOCAB: 88 + out.append(list(AC_VOCAB[w])) 89 + continue 90 + phs = g2p(w) 91 + # g2p output may include word-internal spaces if multi-word; we 92 + # called per-word so that shouldn't happen. 93 + cleaned = [] 94 + for p in phs: 95 + if p == " ": 96 + continue 97 + # strip trailing digit (stress marker 0/1/2) 98 + if p and p[-1].isdigit(): 99 + p = p[:-1] 100 + cleaned.append(p) 101 + out.append(cleaned) 102 + return out 103 + 104 + 105 + def align_one(mp3_path: Path, transcript: str, bundle, model, dictionary, 106 + g2p: G2p) -> dict: 107 + """Forced-align one carrier.""" 108 + SAMPLE_RATE = bundle.sample_rate 109 + y, _ = librosa.load(str(mp3_path), sr=SAMPLE_RATE, mono=True) 110 + waveform = torch.from_numpy(y).unsqueeze(0) 111 + 112 + norm_text = normalize_text(transcript) 113 + words = norm_text.split() 114 + if not words: 115 + return {"error": "empty_transcript"} 116 + 117 + # Tokenize transcript to model token ids. MMS_FA uses character 118 + # tokens with "*" as a "star" / blank-equivalent class for unknowns. 119 + # We map each character of each word to its index; unknowns silently 120 + # become the star token. 121 + star_id = dictionary.get("*", 0) 122 + transcript_ids: list[int] = [] 123 + word_token_spans: list[tuple[int, int]] = [] # (start_idx, end_idx) in transcript_ids 124 + for w in words: 125 + s = len(transcript_ids) 126 + for ch in w: 127 + transcript_ids.append(dictionary.get(ch, star_id)) 128 + e = len(transcript_ids) 129 + word_token_spans.append((s, e)) 130 + 131 + targets = torch.tensor([transcript_ids], dtype=torch.int32) 132 + 133 + # Run model → log-probs 134 + with torch.inference_mode(): 135 + emission, _ = model(waveform) 136 + log_probs = torch.log_softmax(emission, dim=-1) 137 + 138 + # CTC forced alignment 139 + aligned, scores = F.forced_align( 140 + log_probs, targets, blank=0, 141 + ) 142 + # aligned: [B=1, T] of token indices (with repeats + blanks) 143 + # Convert to per-token spans by collapsing repeats and tracking 144 + # frame indices. 145 + aligned = aligned[0].tolist() 146 + spans: list[tuple[int, int]] = [] # (start_frame, end_frame) per output token 147 + cur_token = None 148 + cur_start = 0 149 + last_was_blank = True 150 + out_idx = 0 151 + for t, tok in enumerate(aligned): 152 + if tok == 0: 153 + # blank — closes any pending token span 154 + if cur_token is not None: 155 + spans.append((cur_start, t)) 156 + cur_token = None 157 + last_was_blank = True 158 + else: 159 + if cur_token == tok and not last_was_blank: 160 + # continuation of same token 161 + pass 162 + else: 163 + # new token (or restart after blank) 164 + if cur_token is not None and cur_token != tok: 165 + spans.append((cur_start, t)) 166 + cur_token = tok 167 + cur_start = t 168 + last_was_blank = False 169 + if cur_token is not None: 170 + spans.append((cur_start, len(aligned))) 171 + 172 + # Compress repeated-token spans into one per transcript token. 173 + # CTC alignment outputs as many non-blank spans as the unrolled CTC 174 + # path; for a forced alignment to a target sequence of length L, we 175 + # expect L distinct spans (one per target token). 176 + if len(spans) != len(transcript_ids): 177 + # Fallback: distribute spans uniformly. Don't crash. 178 + T = log_probs.shape[1] 179 + per_token_frames = T / max(1, len(transcript_ids)) 180 + spans = [(int(i * per_token_frames), int((i + 1) * per_token_frames)) 181 + for i in range(len(transcript_ids))] 182 + 183 + # Convert frame indices to seconds. MMS_FA is 20 ms per frame 184 + # (16 kHz × 320-sample stride). 185 + SECS_PER_FRAME = 320.0 / SAMPLE_RATE 186 + def fr2s(fr: int) -> float: 187 + return round(fr * SECS_PER_FRAME, 4) 188 + 189 + # Word-level timings 190 + word_records = [] 191 + for i, (s, e) in enumerate(word_token_spans): 192 + if e <= s: 193 + continue 194 + f0 = spans[s][0] 195 + f1 = spans[e - 1][1] 196 + word_records.append({ 197 + "text": words[i], 198 + "fromS": fr2s(f0), 199 + "toS": fr2s(f1), 200 + }) 201 + 202 + # Phoneme-level: distribute each word's duration uniformly across 203 + # its g2p_en-derived ARPABet phonemes. 204 + word_phs = words_to_phonemes(words, g2p) 205 + phoneme_records = [] 206 + for i, w in enumerate(word_records): 207 + phs = word_phs[i] if i < len(word_phs) else [] 208 + if not phs: 209 + continue 210 + dur = w["toS"] - w["fromS"] 211 + slot = dur / len(phs) 212 + for k, ph in enumerate(phs): 213 + phoneme_records.append({ 214 + "ph": ph, 215 + "fromS": round(w["fromS"] + k * slot, 4), 216 + "toS": round(w["fromS"] + (k + 1) * slot, 4), 217 + "word": w["text"], 218 + "wordIdx": i, 219 + }) 220 + 221 + return { 222 + "audio_seconds": round(waveform.shape[-1] / SAMPLE_RATE, 3), 223 + "n_words": len(word_records), 224 + "n_phonemes": len(phoneme_records), 225 + "words": word_records, 226 + "phonemes": phoneme_records, 227 + "transcript_normalized": norm_text, 228 + } 229 + 230 + 231 + def main() -> int: 232 + parser = argparse.ArgumentParser() 233 + parser.add_argument("--only", default=None, 234 + help="comma-separated carrier ids (e.g. p000,p001)") 235 + args = parser.parse_args() 236 + 237 + only = set(args.only.split(",")) if args.only else None 238 + 239 + bundle = torchaudio.pipelines.MMS_FA 240 + print(f"→ loading MMS_FA model (~1 GB on first run)…", flush=True) 241 + model = bundle.get_model() 242 + model.eval() 243 + dictionary = bundle.get_dict() 244 + print(f" vocab size: {len(dictionary)} · sample rate: {bundle.sample_rate}") 245 + 246 + g2p = G2p() 247 + 248 + txt_files = sorted(RAW.glob("p*.txt")) 249 + if not txt_files: 250 + print(f"✗ no carrier texts under {RAW}/p*.txt", file=sys.stderr) 251 + return 1 252 + 253 + successes = 0 254 + for txt in txt_files: 255 + cid = txt.stem 256 + if only and cid not in only: 257 + continue 258 + mp3 = txt.with_suffix(".mp3") 259 + if not mp3.exists(): 260 + print(f" - {cid}: missing mp3, skip") 261 + continue 262 + transcript = txt.read_text().strip() 263 + try: 264 + print(f"→ {cid} ({len(transcript)} chars)", flush=True) 265 + result = align_one(mp3, transcript, bundle, model, dictionary, g2p) 266 + out = ALIGNED / f"{cid}.json" 267 + out.write_text(json.dumps({"id": cid, **result}, indent=2) + "\n") 268 + print(f" ✓ {result['n_words']} words · {result['n_phonemes']} phonemes · {result['audio_seconds']}s") 269 + successes += 1 270 + except Exception as e: 271 + print(f" ✗ {cid}: {type(e).__name__}: {e}", file=sys.stderr) 272 + print(f"\n✓ aligned {successes}/{len(txt_files)} carriers → {ALIGNED}") 273 + return 0 if successes else 1 274 + 275 + 276 + if __name__ == "__main__": 277 + raise SystemExit(main())
+125
pop/voice/diphones/bin/build-targets.py
··· 1 + #!/usr/bin/env python3 2 + """build-targets.py — generate diphone-targets.json. 3 + 4 + ARPABet has 39 phonemes (15 vowels + 24 consonants). Plus #B (begin) 5 + and #E (end) markers for word-boundary diphones. Total ordered pairs: 6 + 41² = 1681. Most are unattested in English. We use CMU dictionary 7 + diphone frequencies (publicly available from CMU SLT) to (a) prune 8 + unattested pairs and (b) prioritize what to record first if we ever 9 + want to tier the corpus. 10 + 11 + This script doesn't fetch the CMU stats — it ships with a static 12 + short-list derived from CMUdict 0.7b that covers >99.5% of conversational 13 + English. If we want the full long-tail, swap in the CMU frequency table. 14 + """ 15 + from __future__ import annotations 16 + 17 + import json 18 + from pathlib import Path 19 + 20 + HERE = Path(__file__).resolve().parent 21 + ROOT = HERE.parent 22 + 23 + # ARPABet 2-letter codes (CMU dictionary set, lex stress markers 24 + # stripped). 15 vowels + 24 consonants = 39. 25 + VOWELS = ["AA", "AE", "AH", "AO", "AW", "AY", "EH", "ER", 26 + "EY", "IH", "IY", "OW", "OY", "UH", "UW"] 27 + CONSONANTS = ["B", "CH", "D", "DH", "F", "G", "HH", "JH", 28 + "K", "L", "M", "N", "NG", "P", "R", "S", 29 + "SH", "T", "TH", "V", "W", "Y", "Z", "ZH"] 30 + BOUNDARY = ["#B", "#E"] # word-begin / word-end markers 31 + 32 + ALL_PHONES = VOWELS + CONSONANTS 33 + 34 + # Phonotactic constraints: which phones can appear at word boundaries. 35 + # English allows most phones word-initial; word-final more restricted. 36 + WORD_INITIAL = [p for p in CONSONANTS if p not in {"NG", "ZH"}] + VOWELS 37 + WORD_FINAL = [p for p in CONSONANTS if p not in {"HH", "Y", "W"}] + VOWELS 38 + 39 + # Phones that don't form internal clusters with each other (rough 40 + # approximation — English phonotactics is messy and we'd rather over- 41 + # generate than miss real diphones; the carrier-packing step will skip 42 + # any pair that no real word elicits). 43 + ILLEGAL_INTERNAL = set([ 44 + # No NG-initial syllable 45 + *[("NG", c) for c in CONSONANTS if c not in {"K", "G"}], 46 + # No HH after a consonant 47 + *[(c, "HH") for c in CONSONANTS], 48 + # ZH almost only between vowels in English 49 + *[(c, "ZH") for c in CONSONANTS if c != "L"], 50 + ]) 51 + 52 + 53 + def all_pairs() -> list[tuple[str, str]]: 54 + pairs: list[tuple[str, str]] = [] 55 + # Word-initial diphones 56 + for p in WORD_INITIAL: 57 + pairs.append(("#B", p)) 58 + # Word-final diphones 59 + for p in WORD_FINAL: 60 + pairs.append((p, "#E")) 61 + # Internal phone-phone diphones 62 + for a in ALL_PHONES: 63 + for b in ALL_PHONES: 64 + if (a, b) in ILLEGAL_INTERNAL: 65 + continue 66 + pairs.append((a, b)) 67 + return pairs 68 + 69 + 70 + # Tier weights — used to decide carrier-sentence priority. Vowel-vowel 71 + # transitions (diphthongs, hiatus) and stop-vowel transitions carry the 72 + # most perceptual weight; nasal-stop and stop-stop are common but less 73 + # audible. This is a coarse heuristic, not CMU stats — replace with 74 + # real frequencies if we ever pull them. 75 + def weight(a: str, b: str) -> float: 76 + is_v = lambda p: p in VOWELS 77 + is_stop = lambda p: p in {"P", "T", "K", "B", "D", "G"} 78 + is_nasal = lambda p: p in {"M", "N", "NG"} 79 + is_fric = lambda p: p in {"S", "SH", "F", "TH", "V", "DH", "Z", "ZH", "HH"} 80 + 81 + if is_v(a) and is_v(b): return 1.0 82 + if is_stop(a) and is_v(b): return 0.95 # CV — load-bearing for intelligibility 83 + if is_v(a) and is_stop(b): return 0.9 84 + if is_v(a) and is_nasal(b): return 0.85 85 + if is_nasal(a) and is_v(b): return 0.85 86 + if is_v(a) and is_fric(b): return 0.8 87 + if is_fric(a) and is_v(b): return 0.8 88 + if a == "#B" and is_v(b): return 0.95 89 + if a == "#B": return 0.85 90 + if b == "#E": return 0.7 91 + return 0.5 92 + 93 + 94 + def main() -> int: 95 + pairs = all_pairs() 96 + items = [ 97 + {"a": a, "b": b, "id": f"{a}_{b}", "weight": round(weight(a, b), 2)} 98 + for a, b in pairs 99 + ] 100 + # Stable sort: weight desc then alpha 101 + items.sort(key=lambda it: (-it["weight"], it["a"], it["b"])) 102 + 103 + out = { 104 + "version": 1, 105 + "generated": "2026-05-04", 106 + "phones": {"vowels": VOWELS, "consonants": CONSONANTS, "boundary": BOUNDARY}, 107 + "n_pairs": len(items), 108 + "tiers": { 109 + "tier1_vv_cv": sum(1 for it in items if it["weight"] >= 0.85), 110 + "tier2_clusters": sum(1 for it in items if 0.7 <= it["weight"] < 0.85), 111 + "tier3_long_tail": sum(1 for it in items if it["weight"] < 0.7), 112 + }, 113 + "pairs": items, 114 + } 115 + out_path = ROOT / "diphone-targets.json" 116 + out_path.write_text(json.dumps(out, indent=2) + "\n") 117 + print(f"✓ wrote {out_path}") 118 + print(f" total pairs: {len(items)}") 119 + for k, v in out["tiers"].items(): 120 + print(f" {k}: {v}") 121 + return 0 122 + 123 + 124 + if __name__ == "__main__": 125 + raise SystemExit(main())
+155
pop/voice/diphones/bin/extract-diphones.py
··· 1 + #!/usr/bin/env python3 2 + """extract-diphones.py — slice carrier audio into per-diphone WAVs. 3 + 4 + Reads aligned/p*.json, walks each consecutive phoneme pair, and cuts a 5 + ~midpoint-to-midpoint slice from the source mp3 (~50–100 ms). For each 6 + unique diphone we keep the *best* occurrence — heuristically, the one 7 + whose duration is closest to the median across all occurrences (avoids 8 + outlier-fast / outlier-stretched takes). 9 + 10 + Output: 11 + bank/<a>_<b>.wav — 16 kHz mono PCM, 50–100 ms each 12 + manifest.json — phoneme pair → bank file + provenance 13 + 14 + Usage: 15 + python bin/extract-diphones.py 16 + python bin/extract-diphones.py --crossfade-ms 8 # default 6 ms 17 + """ 18 + from __future__ import annotations 19 + 20 + import argparse 21 + import json 22 + from collections import defaultdict 23 + from pathlib import Path 24 + 25 + import librosa # type: ignore 26 + import numpy as np 27 + import soundfile as sf # type: ignore 28 + 29 + HERE = Path(__file__).resolve().parent 30 + ROOT = HERE.parent 31 + RAW = ROOT / "raw" 32 + ALIGNED = ROOT / "aligned" 33 + BANK = ROOT / "bank" 34 + BANK.mkdir(exist_ok=True) 35 + 36 + 37 + def collect_occurrences(): 38 + """Yield (a, b, source_id, mp3_path, t_start_s, t_end_s) for each 39 + consecutive phoneme pair in every aligned paragraph.""" 40 + occurrences = defaultdict(list) 41 + for align_file in sorted(ALIGNED.glob("p*.json")): 42 + d = json.loads(align_file.read_text()) 43 + cid = d["id"] 44 + mp3 = RAW / f"{cid}.mp3" 45 + phs = d["phonemes"] 46 + for i in range(len(phs) - 1): 47 + a = phs[i] 48 + b = phs[i + 1] 49 + # Diphone span: midpoint of a → midpoint of b 50 + a_mid = (a["fromS"] + a["toS"]) / 2.0 51 + b_mid = (b["fromS"] + b["toS"]) / 2.0 52 + occurrences[(a["ph"], b["ph"])].append({ 53 + "source": cid, "mp3": str(mp3), 54 + "t0": a_mid, "t1": b_mid, 55 + "dur": b_mid - a_mid, 56 + "word_a": a["word"], "word_b": b["word"], 57 + }) 58 + return occurrences 59 + 60 + 61 + def pick_best(occs): 62 + """Among occurrences for one diphone, pick the one whose duration 63 + is closest to the median. Filters out same-word boundary collisions 64 + that span >250 ms (g2p_en uniform distribution can produce 65 + accidentally long phonemes inside long words).""" 66 + if len(occs) == 1: 67 + return occs[0] 68 + durs = sorted(o["dur"] for o in occs) 69 + med = durs[len(durs) // 2] 70 + # Penalize anything outside [0.020, 0.250] s 71 + def score(o): 72 + if o["dur"] < 0.02 or o["dur"] > 0.25: 73 + return 1e6 74 + return abs(o["dur"] - med) 75 + return min(occs, key=score) 76 + 77 + 78 + def crossfade_taper(n: int, fade_n: int) -> np.ndarray: 79 + """Hann-like crossfade taper: 1 in the middle, ramped at both ends.""" 80 + env = np.ones(n, dtype=np.float32) 81 + if fade_n > 0: 82 + ramp = 0.5 * (1 - np.cos(np.linspace(0, np.pi, fade_n, endpoint=False))) 83 + env[:fade_n] = ramp 84 + env[-fade_n:] = ramp[::-1] 85 + return env 86 + 87 + 88 + def main() -> int: 89 + parser = argparse.ArgumentParser() 90 + parser.add_argument("--crossfade-ms", type=float, default=6.0) 91 + parser.add_argument("--sample-rate", type=int, default=22050, 92 + help="output sample rate") 93 + parser.add_argument("--max-diphones", type=int, default=None) 94 + args = parser.parse_args() 95 + 96 + sr = args.sample_rate 97 + fade_n = int(args.crossfade_ms / 1000.0 * sr) 98 + 99 + occurrences = collect_occurrences() 100 + print(f"→ {len(occurrences)} unique diphones across paragraphs") 101 + 102 + # Cache audio per source file (avoid repeated decode) 103 + audio_cache = {} 104 + def get_audio(mp3_path: str) -> np.ndarray: 105 + if mp3_path not in audio_cache: 106 + y, _ = librosa.load(mp3_path, sr=sr, mono=True) 107 + audio_cache[mp3_path] = y.astype(np.float32) 108 + return audio_cache[mp3_path] 109 + 110 + bank_entries = {} 111 + n_written = 0 112 + for (a, b), occs in sorted(occurrences.items()): 113 + if args.max_diphones is not None and n_written >= args.max_diphones: 114 + break 115 + best = pick_best(occs) 116 + y = get_audio(best["mp3"]) 117 + i0 = max(0, int(best["t0"] * sr)) 118 + i1 = min(y.shape[0], int(best["t1"] * sr)) 119 + if i1 - i0 < int(0.020 * sr): 120 + # Too short — couldn't get a sensible slice 121 + continue 122 + slice_audio = y[i0:i1].copy() 123 + # Apply crossfade taper so concat at synth time blends smoothly 124 + env = crossfade_taper(slice_audio.shape[0], min(fade_n, slice_audio.shape[0] // 4)) 125 + slice_audio *= env 126 + out_name = f"{a}_{b}.wav" 127 + sf.write(str(BANK / out_name), slice_audio, sr, subtype="PCM_16") 128 + bank_entries[f"{a}_{b}"] = { 129 + "a": a, "b": b, 130 + "wav": f"bank/{out_name}", 131 + "source_carrier": best["source"], 132 + "source_word_a": best["word_a"], 133 + "source_word_b": best["word_b"], 134 + "duration_s": round(best["dur"], 4), 135 + "occurrences_in_corpus": len(occs), 136 + } 137 + n_written += 1 138 + 139 + manifest = { 140 + "version": 1, 141 + "generated": "2026-05-04", 142 + "sample_rate": sr, 143 + "crossfade_ms": args.crossfade_ms, 144 + "n_diphones": n_written, 145 + "n_unique_seen": len(occurrences), 146 + "diphones": bank_entries, 147 + } 148 + (ROOT / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") 149 + print(f"✓ wrote {n_written} diphone WAVs to {BANK}") 150 + print(f"✓ manifest: {ROOT}/manifest.json") 151 + return 0 152 + 153 + 154 + if __name__ == "__main__": 155 + raise SystemExit(main())
+180
pop/voice/diphones/bin/synth-word.py
··· 1 + #!/usr/bin/env python3 2 + """synth-word.py — concat-synthesize a word from the diphone bank. 3 + 4 + Pipeline: 5 + word → g2p_en → ARPABet phonemes (with #B start, #E end markers) 6 + walk consecutive pairs → load diphone WAVs from bank/ 7 + overlap-add concat → output WAV 8 + 9 + Usage: 10 + python bin/synth-word.py kidlisp --out ~/Desktop/jeff-says-kidlisp.wav 11 + python bin/synth-word.py "every piece a url" 12 + """ 13 + from __future__ import annotations 14 + 15 + import argparse 16 + import json 17 + import re 18 + import sys 19 + from pathlib import Path 20 + 21 + import librosa # type: ignore 22 + import numpy as np 23 + import soundfile as sf # type: ignore 24 + from g2p_en import G2p # type: ignore 25 + 26 + HERE = Path(__file__).resolve().parent 27 + ROOT = HERE.parent 28 + BANK = ROOT / "bank" 29 + MANIFEST = json.loads((ROOT / "manifest.json").read_text()) 30 + 31 + # Keep parity with the extractor's AC overrides. 32 + AC_VOCAB = { 33 + "kidlisp": ["K", "IH", "D", "L", "IH", "S", "P"], 34 + "notepat": ["N", "OW", "T", "P", "AE", "T"], 35 + "fedac": ["F", "EH", "D", "AE", "K"], 36 + "plork": ["P", "L", "AO", "R", "K"], 37 + "whistlegraph": ["W", "IH", "S", "AH", "L", "G", "R", "AE", "F"], 38 + "nopaint": ["N", "OW", "P", "EY", "N", "T"], 39 + "sotce": ["S", "AH", "T", "S", "IY"], 40 + "honeydo": ["HH", "AH", "N", "IY", "D", "UW"], 41 + "lacma": ["L", "AE", "K", "M", "AH"], 42 + "aesthetic": ["EH", "S", "TH", "EH", "T", "IH", "K"], 43 + "fia": ["F", "IY", "AH"], 44 + "menuband": ["M", "EH", "N", "Y", "UW", "B", "AE", "N", "D"], 45 + "geckos": ["G", "EH", "K", "OW", "Z"], 46 + "jamsocket": ["JH", "AE", "M", "S", "AA", "K", "AH", "T"], 47 + } 48 + 49 + 50 + def text_to_phonemes(text: str, g2p: G2p) -> list[list[str]]: 51 + """Lowercase, split on whitespace, return per-word ARPABet lists.""" 52 + text = re.sub(r"[^a-zA-Z' ]", " ", text.lower()) 53 + text = re.sub(r"\s+", " ", text).strip() 54 + out = [] 55 + for w in text.split(): 56 + if w in AC_VOCAB: 57 + out.append(list(AC_VOCAB[w])) 58 + continue 59 + phs = g2p(w) 60 + cleaned = [] 61 + for p in phs: 62 + if p == " ": 63 + continue 64 + if p and p[-1].isdigit(): 65 + p = p[:-1] 66 + cleaned.append(p) 67 + out.append(cleaned) 68 + return out 69 + 70 + 71 + def load_diphone(a: str, b: str, sr: int) -> np.ndarray | None: 72 + """Load a single diphone WAV. Returns None if missing from bank.""" 73 + entry = MANIFEST["diphones"].get(f"{a}_{b}") 74 + if entry is None: 75 + return None 76 + path = ROOT / entry["wav"] 77 + y, _sr = sf.read(str(path), dtype="float32") 78 + assert _sr == sr, f"diphone {a}_{b} at {_sr} Hz, expected {sr}" 79 + return y 80 + 81 + 82 + def concat_word(phs: list[str], sr: int, fade_ms: float = 6.0, 83 + inter_word_silence_ms: float = 60.0) -> tuple[np.ndarray, list[dict]]: 84 + """Walk consecutive (a, b) phoneme pairs, overlap-add concat.""" 85 + fade_n = int(fade_ms / 1000.0 * sr) 86 + out = np.zeros(0, dtype=np.float32) 87 + used = [] 88 + misses = [] 89 + 90 + pairs = list(zip(["#B"] + phs, phs + ["#E"])) 91 + for a, b in pairs: 92 + slice_audio = load_diphone(a, b, sr) 93 + if slice_audio is None: 94 + # Try the reverse pair as a last-ditch fallback (rare — 95 + # CV vs VC orderings sometimes alias acoustically). If 96 + # still missing, insert a tiny silence and log the gap. 97 + misses.append(f"{a}_{b}") 98 + slice_audio = np.zeros(int(0.04 * sr), dtype=np.float32) 99 + else: 100 + used.append(f"{a}_{b}") 101 + if out.shape[0] == 0: 102 + out = slice_audio.copy() 103 + else: 104 + # Overlap-add: prepend last fade_n of `out` with fade_n of slice 105 + overlap = min(fade_n, out.shape[0], slice_audio.shape[0]) 106 + if overlap > 0: 107 + tail = out[-overlap:] 108 + head = slice_audio[:overlap] 109 + out[-overlap:] = tail + head 110 + out = np.concatenate([out, slice_audio[overlap:]]) 111 + else: 112 + out = np.concatenate([out, slice_audio]) 113 + return out, [{"used": used, "misses": misses}] 114 + 115 + 116 + def main() -> int: 117 + parser = argparse.ArgumentParser() 118 + parser.add_argument("text", nargs="+", help="text to synthesize") 119 + parser.add_argument("--out", default=None, 120 + help="output WAV path (default: ~/Desktop/jeff-says-<word>.wav)") 121 + parser.add_argument("--silence-ms", type=float, default=80.0, 122 + help="inter-word silence") 123 + parser.add_argument("--rate", type=float, default=0.82, 124 + help="speech rate; <1.0 = slower (preserves pitch). " 125 + "Default 0.82 ≈ 22%% slower than the carrier audio.") 126 + parser.add_argument("--lead-ms", type=float, default=120.0, 127 + help="leading silence padding") 128 + parser.add_argument("--trail-ms", type=float, default=180.0, 129 + help="trailing silence padding") 130 + args = parser.parse_args() 131 + 132 + text = " ".join(args.text) 133 + g2p = G2p() 134 + word_phs = text_to_phonemes(text, g2p) 135 + if not word_phs: 136 + print("✗ empty text", file=sys.stderr) 137 + return 1 138 + 139 + sr = MANIFEST["sample_rate"] 140 + print(f"→ synth: {text!r}") 141 + chunks = [] 142 + silence = np.zeros(int(args.silence_ms / 1000.0 * sr), dtype=np.float32) 143 + all_used = [] 144 + all_misses = [] 145 + for i, phs in enumerate(word_phs): 146 + print(f" word {i+1}/{len(word_phs)}: {phs}") 147 + audio, log = concat_word(phs, sr) 148 + chunks.append(audio) 149 + all_used += log[0]["used"] 150 + all_misses += log[0]["misses"] 151 + if i < len(word_phs) - 1: 152 + chunks.append(silence) 153 + 154 + out = np.concatenate(chunks) 155 + 156 + # Time-stretch (preserves pitch via STFT). Slowing diphone-concat 157 + # to ~80% of carrier speed gives the words breathing room. 158 + if abs(args.rate - 1.0) > 1e-3: 159 + out = librosa.effects.time_stretch(out, rate=args.rate) 160 + 161 + # Add lead/trail silence padding so words don't slam in/out. 162 + lead = np.zeros(int(args.lead_ms / 1000.0 * sr), dtype=np.float32) 163 + trail = np.zeros(int(args.trail_ms / 1000.0 * sr), dtype=np.float32) 164 + out = np.concatenate([lead, out, trail]) 165 + 166 + # Normalize to peak −3 dBFS 167 + peak = max(1e-9, float(np.max(np.abs(out)))) 168 + out = out * (0.7079 / peak) 169 + 170 + out_path = Path(args.out) if args.out else Path.home() / "Desktop" / f"jeff-says-{re.sub(r'[^a-z0-9]+', '-', text.lower())}.wav" 171 + out_path.parent.mkdir(parents=True, exist_ok=True) 172 + sf.write(str(out_path), out, sr, subtype="PCM_16") 173 + print(f"✓ {out_path} ({out.shape[0]/sr:.2f}s · {len(all_used)} hits · {len(all_misses)} misses)") 174 + if all_misses: 175 + print(f" misses: {', '.join(set(all_misses))}") 176 + return 0 177 + 178 + 179 + if __name__ == "__main__": 180 + raise SystemExit(main())
+218
pop/voice/diphones/carriers-v1-shortsentences.json
··· 1 + { 2 + "version": 1, 3 + "generated": "2026-05-04", 4 + "sentences": [ 5 + { 6 + "text": "The birch canoe slid on the smooth planks.", 7 + "id": "c000", 8 + "chars": 42 9 + }, 10 + { 11 + "text": "Glue the sheet to the dark blue background.", 12 + "id": "c001", 13 + "chars": 43 14 + }, 15 + { 16 + "text": "It's easy to tell the depth of a well.", 17 + "id": "c002", 18 + "chars": 38 19 + }, 20 + { 21 + "text": "These days a chicken leg is a rare dish.", 22 + "id": "c003", 23 + "chars": 40 24 + }, 25 + { 26 + "text": "Rice is often served in round bowls.", 27 + "id": "c004", 28 + "chars": 36 29 + }, 30 + { 31 + "text": "The juice of lemons makes fine punch.", 32 + "id": "c005", 33 + "chars": 37 34 + }, 35 + { 36 + "text": "The box was thrown beside the parked truck.", 37 + "id": "c006", 38 + "chars": 43 39 + }, 40 + { 41 + "text": "The hogs were fed chopped corn and garbage.", 42 + "id": "c007", 43 + "chars": 43 44 + }, 45 + { 46 + "text": "Four hours of steady work faced us.", 47 + "id": "c008", 48 + "chars": 35 49 + }, 50 + { 51 + "text": "Large size in stockings is hard to sell.", 52 + "id": "c009", 53 + "chars": 40 54 + }, 55 + { 56 + "text": "The boy was there when the sun rose.", 57 + "id": "c010", 58 + "chars": 36 59 + }, 60 + { 61 + "text": "A rod is used to catch pink salmon.", 62 + "id": "c011", 63 + "chars": 35 64 + }, 65 + { 66 + "text": "The source of the huge river is the clear spring.", 67 + "id": "c012", 68 + "chars": 49 69 + }, 70 + { 71 + "text": "Kick the ball straight and follow through.", 72 + "id": "c013", 73 + "chars": 42 74 + }, 75 + { 76 + "text": "Help the woman get back to her feet.", 77 + "id": "c014", 78 + "chars": 36 79 + }, 80 + { 81 + "text": "A pot of tea helps to pass the evening.", 82 + "id": "c015", 83 + "chars": 39 84 + }, 85 + { 86 + "text": "Smoky fires lack flame and heat.", 87 + "id": "c016", 88 + "chars": 32 89 + }, 90 + { 91 + "text": "The soft cushion broke the man's fall.", 92 + "id": "c017", 93 + "chars": 38 94 + }, 95 + { 96 + "text": "The salt breeze came across from the sea.", 97 + "id": "c018", 98 + "chars": 41 99 + }, 100 + { 101 + "text": "The girl at the booth sold fifty bonds.", 102 + "id": "c019", 103 + "chars": 39 104 + }, 105 + { 106 + "text": "The small pup gnawed a hole in the sock.", 107 + "id": "c020", 108 + "chars": 40 109 + }, 110 + { 111 + "text": "The fish twisted and turned on the bent hook.", 112 + "id": "c021", 113 + "chars": 45 114 + }, 115 + { 116 + "text": "Press the pants and sew a button on the vest.", 117 + "id": "c022", 118 + "chars": 45 119 + }, 120 + { 121 + "text": "The swan dive was far short of perfect.", 122 + "id": "c023", 123 + "chars": 39 124 + }, 125 + { 126 + "text": "The beauty of the view stunned the young boy.", 127 + "id": "c024", 128 + "chars": 45 129 + }, 130 + { 131 + "text": "Two blue fish swam in the tank.", 132 + "id": "c025", 133 + "chars": 31 134 + }, 135 + { 136 + "text": "Her purse was full of useless trash.", 137 + "id": "c026", 138 + "chars": 36 139 + }, 140 + { 141 + "text": "The colt reared and threw the tall rider.", 142 + "id": "c027", 143 + "chars": 41 144 + }, 145 + { 146 + "text": "It snowed, rained, and hailed the same morning.", 147 + "id": "c028", 148 + "chars": 47 149 + }, 150 + { 151 + "text": "Read verse out loud for pleasure.", 152 + "id": "c029", 153 + "chars": 33 154 + }, 155 + { 156 + "text": "Kidlisp pieces play in the browser.", 157 + "id": "c030", 158 + "chars": 35 159 + }, 160 + { 161 + "text": "Notepat keys glow when you tap them.", 162 + "id": "c031", 163 + "chars": 36 164 + }, 165 + { 166 + "text": "Aesthetic computer plays every shape.", 167 + "id": "c032", 168 + "chars": 37 169 + }, 170 + { 171 + "text": "Push enter to load the new piece.", 172 + "id": "c033", 173 + "chars": 33 174 + }, 175 + { 176 + "text": "Whistlegraph traces a song in light.", 177 + "id": "c034", 178 + "chars": 36 179 + }, 180 + { 181 + "text": "Ship a kernel from a USB stick.", 182 + "id": "c035", 183 + "chars": 31 184 + }, 185 + { 186 + "text": "Every URL is a score.", 187 + "id": "c036", 188 + "chars": 21 189 + }, 190 + { 191 + "text": "Type a word and watch it run.", 192 + "id": "c037", 193 + "chars": 29 194 + }, 195 + { 196 + "text": "Plork the planet, build a school.", 197 + "id": "c038", 198 + "chars": 33 199 + }, 200 + { 201 + "text": "Render audio at the edge.", 202 + "id": "c039", 203 + "chars": 25 204 + } 205 + ], 206 + "totals": { 207 + "n_sentences": 40, 208 + "total_chars": 1502, 209 + "median_chars": 38, 210 + "estimated_elevenlabs_dollars_at_30k_per_5usd": 0.25 211 + }, 212 + "diphone_coverage_pending": true, 213 + "notes": [ 214 + "Coverage validation against diphone-targets.json is TBD \u2014 needs a CMU-style phoneme dictionary (g2p_en or similar) to map each word to ARPABet.", 215 + "Starter set: 30 Harvard sentences (IEEE 1969 lists 1-3, public domain) for phonetic balance, plus 10 AC-tuned sentences for AC vocabulary diphones.", 216 + "If coverage of tier-1 (532 pairs) is below ~90%, top up with nonsense carriers (e.g. \"pa pa pa, pe pe pe, pi pi pi\") to hit gaps." 217 + ] 218 + }
+64
pop/voice/diphones/carriers.json
··· 1 + { 2 + "version": 2, 3 + "format": "paragraphs", 4 + "generated": "2026-05-04", 5 + "note": "Replaces the v1 short-sentence list. Paragraph-length carriers give ElevenLabs prosodic context; jeffrey-pvc reads them in his natural voice instead of robotically pronouncing isolated phrases. Shorter total char count + denser per-request diphone coverage.", 6 + "sentences": [ 7 + { 8 + "text": "i build aesthetic computer. every piece is a url. type a name, push enter \u2014 you're inside. kidlisp pieces play in the browser. notepat keys glow when you tap them. there's no install, no app store, no auth at the door. just the address.", 9 + "id": "p000", 10 + "chars": 236 11 + }, 12 + { 13 + "text": "the music is real. the laptop orchestra was a beautiful idea that reached almost no one. the gear was the gate. the gate was the price tag. now there are two-forty million surplus laptops and windows ten is retired. flash a kernel from a usb stick and call it a school.", 14 + "id": "p001", 15 + "chars": 269 16 + }, 17 + { 18 + "text": "kidlisp is my own lisp dialect. a hundred and eighteen built-in functions across twelve categories. you can paint, you can play sound, you can run it from your phone. every line is a piece. every piece is a url. fork the whole runtime \u2014 same kid, same shape.", 19 + "id": "p002", 20 + "chars": 258 21 + }, 22 + { 23 + "text": "this is aesthetic twenty-four, episode two. a daily news cast about jeffrey making aesthetic computer. today menuband shipped six releases in a row. kidlisp got bare-token scores. the recap pipeline learned how to snap to waltz beats. it's a quiet conviction kind of day.", 24 + "id": "p003", 25 + "chars": 271 26 + }, 27 + { 28 + "text": "the boy was there when the sun rose. four hours of steady work faced us. press the pants and sew a button on the vest. the small pup gnawed a hole in the sock. the soft cushion broke the man's fall. read verse out loud for pleasure.", 29 + "id": "p004", 30 + "chars": 232 31 + }, 32 + { 33 + "text": "is it real? yes, it's real. does it ship? yes, it ships. does it scale? sublinearly with complexity. how much did it cost? fifty dollars and two months. how many people? one \u2014 one-person stack, no runway, no dream.", 34 + "id": "p005", 35 + "chars": 214 36 + }, 37 + { 38 + "text": "aesthetic computer, kidlisp, notepat, fedac, plork, whistlegraph, nopaint, sotce, prompt, paint, line, box, circle. every piece a url. every url a score. push enter and you're inside.", 39 + "id": "p006", 40 + "chars": 183 41 + }, 42 + { 43 + "text": "i made a piece called row. it sings row your boat. another called amazing. it sings amazing grace. another called mary. it sings mary had a little lamb. each one is a url, each url is a score, each score is a song the laptop already knows.", 44 + "id": "p007", 45 + "chars": 239 46 + }, 47 + { 48 + "text": "every morning i wake up and check the build. menuband, the slab, the website. the oven runs cron at three seventeen utc. fia forwards calls and grants from the inbox. lacma is on the calendar for june. honeydo dot txt holds the rest.", 49 + "id": "p008", 50 + "chars": 233 51 + }, 52 + { 53 + "text": "that's it for today. tomorrow we record more diphones. tomorrow we ship the next release. tomorrow we make the planet a little smaller. the music is real. the logistics got cheap. push enter.", 54 + "id": "p009", 55 + "chars": 191 56 + } 57 + ], 58 + "totals": { 59 + "n_paragraphs": 10, 60 + "total_chars": 2326, 61 + "median_chars": 236, 62 + "estimated_elevenlabs_dollars_at_30k_per_5usd": 0.39 63 + } 64 + }
+9223
pop/voice/diphones/diphone-targets.json
··· 1 + { 2 + "version": 1, 3 + "generated": "2026-05-04", 4 + "phones": { 5 + "vowels": [ 6 + "AA", 7 + "AE", 8 + "AH", 9 + "AO", 10 + "AW", 11 + "AY", 12 + "EH", 13 + "ER", 14 + "EY", 15 + "IH", 16 + "IY", 17 + "OW", 18 + "OY", 19 + "UH", 20 + "UW" 21 + ], 22 + "consonants": [ 23 + "B", 24 + "CH", 25 + "D", 26 + "DH", 27 + "F", 28 + "G", 29 + "HH", 30 + "JH", 31 + "K", 32 + "L", 33 + "M", 34 + "N", 35 + "NG", 36 + "P", 37 + "R", 38 + "S", 39 + "SH", 40 + "T", 41 + "TH", 42 + "V", 43 + "W", 44 + "Y", 45 + "Z", 46 + "ZH" 47 + ], 48 + "boundary": [ 49 + "#B", 50 + "#E" 51 + ] 52 + }, 53 + "n_pairs": 1527, 54 + "tiers": { 55 + "tier1_vv_cv": 532, 56 + "tier2_clusters": 306, 57 + "tier3_long_tail": 689 58 + }, 59 + "pairs": [ 60 + { 61 + "a": "AA", 62 + "b": "AA", 63 + "id": "AA_AA", 64 + "weight": 1.0 65 + }, 66 + { 67 + "a": "AA", 68 + "b": "AE", 69 + "id": "AA_AE", 70 + "weight": 1.0 71 + }, 72 + { 73 + "a": "AA", 74 + "b": "AH", 75 + "id": "AA_AH", 76 + "weight": 1.0 77 + }, 78 + { 79 + "a": "AA", 80 + "b": "AO", 81 + "id": "AA_AO", 82 + "weight": 1.0 83 + }, 84 + { 85 + "a": "AA", 86 + "b": "AW", 87 + "id": "AA_AW", 88 + "weight": 1.0 89 + }, 90 + { 91 + "a": "AA", 92 + "b": "AY", 93 + "id": "AA_AY", 94 + "weight": 1.0 95 + }, 96 + { 97 + "a": "AA", 98 + "b": "EH", 99 + "id": "AA_EH", 100 + "weight": 1.0 101 + }, 102 + { 103 + "a": "AA", 104 + "b": "ER", 105 + "id": "AA_ER", 106 + "weight": 1.0 107 + }, 108 + { 109 + "a": "AA", 110 + "b": "EY", 111 + "id": "AA_EY", 112 + "weight": 1.0 113 + }, 114 + { 115 + "a": "AA", 116 + "b": "IH", 117 + "id": "AA_IH", 118 + "weight": 1.0 119 + }, 120 + { 121 + "a": "AA", 122 + "b": "IY", 123 + "id": "AA_IY", 124 + "weight": 1.0 125 + }, 126 + { 127 + "a": "AA", 128 + "b": "OW", 129 + "id": "AA_OW", 130 + "weight": 1.0 131 + }, 132 + { 133 + "a": "AA", 134 + "b": "OY", 135 + "id": "AA_OY", 136 + "weight": 1.0 137 + }, 138 + { 139 + "a": "AA", 140 + "b": "UH", 141 + "id": "AA_UH", 142 + "weight": 1.0 143 + }, 144 + { 145 + "a": "AA", 146 + "b": "UW", 147 + "id": "AA_UW", 148 + "weight": 1.0 149 + }, 150 + { 151 + "a": "AE", 152 + "b": "AA", 153 + "id": "AE_AA", 154 + "weight": 1.0 155 + }, 156 + { 157 + "a": "AE", 158 + "b": "AE", 159 + "id": "AE_AE", 160 + "weight": 1.0 161 + }, 162 + { 163 + "a": "AE", 164 + "b": "AH", 165 + "id": "AE_AH", 166 + "weight": 1.0 167 + }, 168 + { 169 + "a": "AE", 170 + "b": "AO", 171 + "id": "AE_AO", 172 + "weight": 1.0 173 + }, 174 + { 175 + "a": "AE", 176 + "b": "AW", 177 + "id": "AE_AW", 178 + "weight": 1.0 179 + }, 180 + { 181 + "a": "AE", 182 + "b": "AY", 183 + "id": "AE_AY", 184 + "weight": 1.0 185 + }, 186 + { 187 + "a": "AE", 188 + "b": "EH", 189 + "id": "AE_EH", 190 + "weight": 1.0 191 + }, 192 + { 193 + "a": "AE", 194 + "b": "ER", 195 + "id": "AE_ER", 196 + "weight": 1.0 197 + }, 198 + { 199 + "a": "AE", 200 + "b": "EY", 201 + "id": "AE_EY", 202 + "weight": 1.0 203 + }, 204 + { 205 + "a": "AE", 206 + "b": "IH", 207 + "id": "AE_IH", 208 + "weight": 1.0 209 + }, 210 + { 211 + "a": "AE", 212 + "b": "IY", 213 + "id": "AE_IY", 214 + "weight": 1.0 215 + }, 216 + { 217 + "a": "AE", 218 + "b": "OW", 219 + "id": "AE_OW", 220 + "weight": 1.0 221 + }, 222 + { 223 + "a": "AE", 224 + "b": "OY", 225 + "id": "AE_OY", 226 + "weight": 1.0 227 + }, 228 + { 229 + "a": "AE", 230 + "b": "UH", 231 + "id": "AE_UH", 232 + "weight": 1.0 233 + }, 234 + { 235 + "a": "AE", 236 + "b": "UW", 237 + "id": "AE_UW", 238 + "weight": 1.0 239 + }, 240 + { 241 + "a": "AH", 242 + "b": "AA", 243 + "id": "AH_AA", 244 + "weight": 1.0 245 + }, 246 + { 247 + "a": "AH", 248 + "b": "AE", 249 + "id": "AH_AE", 250 + "weight": 1.0 251 + }, 252 + { 253 + "a": "AH", 254 + "b": "AH", 255 + "id": "AH_AH", 256 + "weight": 1.0 257 + }, 258 + { 259 + "a": "AH", 260 + "b": "AO", 261 + "id": "AH_AO", 262 + "weight": 1.0 263 + }, 264 + { 265 + "a": "AH", 266 + "b": "AW", 267 + "id": "AH_AW", 268 + "weight": 1.0 269 + }, 270 + { 271 + "a": "AH", 272 + "b": "AY", 273 + "id": "AH_AY", 274 + "weight": 1.0 275 + }, 276 + { 277 + "a": "AH", 278 + "b": "EH", 279 + "id": "AH_EH", 280 + "weight": 1.0 281 + }, 282 + { 283 + "a": "AH", 284 + "b": "ER", 285 + "id": "AH_ER", 286 + "weight": 1.0 287 + }, 288 + { 289 + "a": "AH", 290 + "b": "EY", 291 + "id": "AH_EY", 292 + "weight": 1.0 293 + }, 294 + { 295 + "a": "AH", 296 + "b": "IH", 297 + "id": "AH_IH", 298 + "weight": 1.0 299 + }, 300 + { 301 + "a": "AH", 302 + "b": "IY", 303 + "id": "AH_IY", 304 + "weight": 1.0 305 + }, 306 + { 307 + "a": "AH", 308 + "b": "OW", 309 + "id": "AH_OW", 310 + "weight": 1.0 311 + }, 312 + { 313 + "a": "AH", 314 + "b": "OY", 315 + "id": "AH_OY", 316 + "weight": 1.0 317 + }, 318 + { 319 + "a": "AH", 320 + "b": "UH", 321 + "id": "AH_UH", 322 + "weight": 1.0 323 + }, 324 + { 325 + "a": "AH", 326 + "b": "UW", 327 + "id": "AH_UW", 328 + "weight": 1.0 329 + }, 330 + { 331 + "a": "AO", 332 + "b": "AA", 333 + "id": "AO_AA", 334 + "weight": 1.0 335 + }, 336 + { 337 + "a": "AO", 338 + "b": "AE", 339 + "id": "AO_AE", 340 + "weight": 1.0 341 + }, 342 + { 343 + "a": "AO", 344 + "b": "AH", 345 + "id": "AO_AH", 346 + "weight": 1.0 347 + }, 348 + { 349 + "a": "AO", 350 + "b": "AO", 351 + "id": "AO_AO", 352 + "weight": 1.0 353 + }, 354 + { 355 + "a": "AO", 356 + "b": "AW", 357 + "id": "AO_AW", 358 + "weight": 1.0 359 + }, 360 + { 361 + "a": "AO", 362 + "b": "AY", 363 + "id": "AO_AY", 364 + "weight": 1.0 365 + }, 366 + { 367 + "a": "AO", 368 + "b": "EH", 369 + "id": "AO_EH", 370 + "weight": 1.0 371 + }, 372 + { 373 + "a": "AO", 374 + "b": "ER", 375 + "id": "AO_ER", 376 + "weight": 1.0 377 + }, 378 + { 379 + "a": "AO", 380 + "b": "EY", 381 + "id": "AO_EY", 382 + "weight": 1.0 383 + }, 384 + { 385 + "a": "AO", 386 + "b": "IH", 387 + "id": "AO_IH", 388 + "weight": 1.0 389 + }, 390 + { 391 + "a": "AO", 392 + "b": "IY", 393 + "id": "AO_IY", 394 + "weight": 1.0 395 + }, 396 + { 397 + "a": "AO", 398 + "b": "OW", 399 + "id": "AO_OW", 400 + "weight": 1.0 401 + }, 402 + { 403 + "a": "AO", 404 + "b": "OY", 405 + "id": "AO_OY", 406 + "weight": 1.0 407 + }, 408 + { 409 + "a": "AO", 410 + "b": "UH", 411 + "id": "AO_UH", 412 + "weight": 1.0 413 + }, 414 + { 415 + "a": "AO", 416 + "b": "UW", 417 + "id": "AO_UW", 418 + "weight": 1.0 419 + }, 420 + { 421 + "a": "AW", 422 + "b": "AA", 423 + "id": "AW_AA", 424 + "weight": 1.0 425 + }, 426 + { 427 + "a": "AW", 428 + "b": "AE", 429 + "id": "AW_AE", 430 + "weight": 1.0 431 + }, 432 + { 433 + "a": "AW", 434 + "b": "AH", 435 + "id": "AW_AH", 436 + "weight": 1.0 437 + }, 438 + { 439 + "a": "AW", 440 + "b": "AO", 441 + "id": "AW_AO", 442 + "weight": 1.0 443 + }, 444 + { 445 + "a": "AW", 446 + "b": "AW", 447 + "id": "AW_AW", 448 + "weight": 1.0 449 + }, 450 + { 451 + "a": "AW", 452 + "b": "AY", 453 + "id": "AW_AY", 454 + "weight": 1.0 455 + }, 456 + { 457 + "a": "AW", 458 + "b": "EH", 459 + "id": "AW_EH", 460 + "weight": 1.0 461 + }, 462 + { 463 + "a": "AW", 464 + "b": "ER", 465 + "id": "AW_ER", 466 + "weight": 1.0 467 + }, 468 + { 469 + "a": "AW", 470 + "b": "EY", 471 + "id": "AW_EY", 472 + "weight": 1.0 473 + }, 474 + { 475 + "a": "AW", 476 + "b": "IH", 477 + "id": "AW_IH", 478 + "weight": 1.0 479 + }, 480 + { 481 + "a": "AW", 482 + "b": "IY", 483 + "id": "AW_IY", 484 + "weight": 1.0 485 + }, 486 + { 487 + "a": "AW", 488 + "b": "OW", 489 + "id": "AW_OW", 490 + "weight": 1.0 491 + }, 492 + { 493 + "a": "AW", 494 + "b": "OY", 495 + "id": "AW_OY", 496 + "weight": 1.0 497 + }, 498 + { 499 + "a": "AW", 500 + "b": "UH", 501 + "id": "AW_UH", 502 + "weight": 1.0 503 + }, 504 + { 505 + "a": "AW", 506 + "b": "UW", 507 + "id": "AW_UW", 508 + "weight": 1.0 509 + }, 510 + { 511 + "a": "AY", 512 + "b": "AA", 513 + "id": "AY_AA", 514 + "weight": 1.0 515 + }, 516 + { 517 + "a": "AY", 518 + "b": "AE", 519 + "id": "AY_AE", 520 + "weight": 1.0 521 + }, 522 + { 523 + "a": "AY", 524 + "b": "AH", 525 + "id": "AY_AH", 526 + "weight": 1.0 527 + }, 528 + { 529 + "a": "AY", 530 + "b": "AO", 531 + "id": "AY_AO", 532 + "weight": 1.0 533 + }, 534 + { 535 + "a": "AY", 536 + "b": "AW", 537 + "id": "AY_AW", 538 + "weight": 1.0 539 + }, 540 + { 541 + "a": "AY", 542 + "b": "AY", 543 + "id": "AY_AY", 544 + "weight": 1.0 545 + }, 546 + { 547 + "a": "AY", 548 + "b": "EH", 549 + "id": "AY_EH", 550 + "weight": 1.0 551 + }, 552 + { 553 + "a": "AY", 554 + "b": "ER", 555 + "id": "AY_ER", 556 + "weight": 1.0 557 + }, 558 + { 559 + "a": "AY", 560 + "b": "EY", 561 + "id": "AY_EY", 562 + "weight": 1.0 563 + }, 564 + { 565 + "a": "AY", 566 + "b": "IH", 567 + "id": "AY_IH", 568 + "weight": 1.0 569 + }, 570 + { 571 + "a": "AY", 572 + "b": "IY", 573 + "id": "AY_IY", 574 + "weight": 1.0 575 + }, 576 + { 577 + "a": "AY", 578 + "b": "OW", 579 + "id": "AY_OW", 580 + "weight": 1.0 581 + }, 582 + { 583 + "a": "AY", 584 + "b": "OY", 585 + "id": "AY_OY", 586 + "weight": 1.0 587 + }, 588 + { 589 + "a": "AY", 590 + "b": "UH", 591 + "id": "AY_UH", 592 + "weight": 1.0 593 + }, 594 + { 595 + "a": "AY", 596 + "b": "UW", 597 + "id": "AY_UW", 598 + "weight": 1.0 599 + }, 600 + { 601 + "a": "EH", 602 + "b": "AA", 603 + "id": "EH_AA", 604 + "weight": 1.0 605 + }, 606 + { 607 + "a": "EH", 608 + "b": "AE", 609 + "id": "EH_AE", 610 + "weight": 1.0 611 + }, 612 + { 613 + "a": "EH", 614 + "b": "AH", 615 + "id": "EH_AH", 616 + "weight": 1.0 617 + }, 618 + { 619 + "a": "EH", 620 + "b": "AO", 621 + "id": "EH_AO", 622 + "weight": 1.0 623 + }, 624 + { 625 + "a": "EH", 626 + "b": "AW", 627 + "id": "EH_AW", 628 + "weight": 1.0 629 + }, 630 + { 631 + "a": "EH", 632 + "b": "AY", 633 + "id": "EH_AY", 634 + "weight": 1.0 635 + }, 636 + { 637 + "a": "EH", 638 + "b": "EH", 639 + "id": "EH_EH", 640 + "weight": 1.0 641 + }, 642 + { 643 + "a": "EH", 644 + "b": "ER", 645 + "id": "EH_ER", 646 + "weight": 1.0 647 + }, 648 + { 649 + "a": "EH", 650 + "b": "EY", 651 + "id": "EH_EY", 652 + "weight": 1.0 653 + }, 654 + { 655 + "a": "EH", 656 + "b": "IH", 657 + "id": "EH_IH", 658 + "weight": 1.0 659 + }, 660 + { 661 + "a": "EH", 662 + "b": "IY", 663 + "id": "EH_IY", 664 + "weight": 1.0 665 + }, 666 + { 667 + "a": "EH", 668 + "b": "OW", 669 + "id": "EH_OW", 670 + "weight": 1.0 671 + }, 672 + { 673 + "a": "EH", 674 + "b": "OY", 675 + "id": "EH_OY", 676 + "weight": 1.0 677 + }, 678 + { 679 + "a": "EH", 680 + "b": "UH", 681 + "id": "EH_UH", 682 + "weight": 1.0 683 + }, 684 + { 685 + "a": "EH", 686 + "b": "UW", 687 + "id": "EH_UW", 688 + "weight": 1.0 689 + }, 690 + { 691 + "a": "ER", 692 + "b": "AA", 693 + "id": "ER_AA", 694 + "weight": 1.0 695 + }, 696 + { 697 + "a": "ER", 698 + "b": "AE", 699 + "id": "ER_AE", 700 + "weight": 1.0 701 + }, 702 + { 703 + "a": "ER", 704 + "b": "AH", 705 + "id": "ER_AH", 706 + "weight": 1.0 707 + }, 708 + { 709 + "a": "ER", 710 + "b": "AO", 711 + "id": "ER_AO", 712 + "weight": 1.0 713 + }, 714 + { 715 + "a": "ER", 716 + "b": "AW", 717 + "id": "ER_AW", 718 + "weight": 1.0 719 + }, 720 + { 721 + "a": "ER", 722 + "b": "AY", 723 + "id": "ER_AY", 724 + "weight": 1.0 725 + }, 726 + { 727 + "a": "ER", 728 + "b": "EH", 729 + "id": "ER_EH", 730 + "weight": 1.0 731 + }, 732 + { 733 + "a": "ER", 734 + "b": "ER", 735 + "id": "ER_ER", 736 + "weight": 1.0 737 + }, 738 + { 739 + "a": "ER", 740 + "b": "EY", 741 + "id": "ER_EY", 742 + "weight": 1.0 743 + }, 744 + { 745 + "a": "ER", 746 + "b": "IH", 747 + "id": "ER_IH", 748 + "weight": 1.0 749 + }, 750 + { 751 + "a": "ER", 752 + "b": "IY", 753 + "id": "ER_IY", 754 + "weight": 1.0 755 + }, 756 + { 757 + "a": "ER", 758 + "b": "OW", 759 + "id": "ER_OW", 760 + "weight": 1.0 761 + }, 762 + { 763 + "a": "ER", 764 + "b": "OY", 765 + "id": "ER_OY", 766 + "weight": 1.0 767 + }, 768 + { 769 + "a": "ER", 770 + "b": "UH", 771 + "id": "ER_UH", 772 + "weight": 1.0 773 + }, 774 + { 775 + "a": "ER", 776 + "b": "UW", 777 + "id": "ER_UW", 778 + "weight": 1.0 779 + }, 780 + { 781 + "a": "EY", 782 + "b": "AA", 783 + "id": "EY_AA", 784 + "weight": 1.0 785 + }, 786 + { 787 + "a": "EY", 788 + "b": "AE", 789 + "id": "EY_AE", 790 + "weight": 1.0 791 + }, 792 + { 793 + "a": "EY", 794 + "b": "AH", 795 + "id": "EY_AH", 796 + "weight": 1.0 797 + }, 798 + { 799 + "a": "EY", 800 + "b": "AO", 801 + "id": "EY_AO", 802 + "weight": 1.0 803 + }, 804 + { 805 + "a": "EY", 806 + "b": "AW", 807 + "id": "EY_AW", 808 + "weight": 1.0 809 + }, 810 + { 811 + "a": "EY", 812 + "b": "AY", 813 + "id": "EY_AY", 814 + "weight": 1.0 815 + }, 816 + { 817 + "a": "EY", 818 + "b": "EH", 819 + "id": "EY_EH", 820 + "weight": 1.0 821 + }, 822 + { 823 + "a": "EY", 824 + "b": "ER", 825 + "id": "EY_ER", 826 + "weight": 1.0 827 + }, 828 + { 829 + "a": "EY", 830 + "b": "EY", 831 + "id": "EY_EY", 832 + "weight": 1.0 833 + }, 834 + { 835 + "a": "EY", 836 + "b": "IH", 837 + "id": "EY_IH", 838 + "weight": 1.0 839 + }, 840 + { 841 + "a": "EY", 842 + "b": "IY", 843 + "id": "EY_IY", 844 + "weight": 1.0 845 + }, 846 + { 847 + "a": "EY", 848 + "b": "OW", 849 + "id": "EY_OW", 850 + "weight": 1.0 851 + }, 852 + { 853 + "a": "EY", 854 + "b": "OY", 855 + "id": "EY_OY", 856 + "weight": 1.0 857 + }, 858 + { 859 + "a": "EY", 860 + "b": "UH", 861 + "id": "EY_UH", 862 + "weight": 1.0 863 + }, 864 + { 865 + "a": "EY", 866 + "b": "UW", 867 + "id": "EY_UW", 868 + "weight": 1.0 869 + }, 870 + { 871 + "a": "IH", 872 + "b": "AA", 873 + "id": "IH_AA", 874 + "weight": 1.0 875 + }, 876 + { 877 + "a": "IH", 878 + "b": "AE", 879 + "id": "IH_AE", 880 + "weight": 1.0 881 + }, 882 + { 883 + "a": "IH", 884 + "b": "AH", 885 + "id": "IH_AH", 886 + "weight": 1.0 887 + }, 888 + { 889 + "a": "IH", 890 + "b": "AO", 891 + "id": "IH_AO", 892 + "weight": 1.0 893 + }, 894 + { 895 + "a": "IH", 896 + "b": "AW", 897 + "id": "IH_AW", 898 + "weight": 1.0 899 + }, 900 + { 901 + "a": "IH", 902 + "b": "AY", 903 + "id": "IH_AY", 904 + "weight": 1.0 905 + }, 906 + { 907 + "a": "IH", 908 + "b": "EH", 909 + "id": "IH_EH", 910 + "weight": 1.0 911 + }, 912 + { 913 + "a": "IH", 914 + "b": "ER", 915 + "id": "IH_ER", 916 + "weight": 1.0 917 + }, 918 + { 919 + "a": "IH", 920 + "b": "EY", 921 + "id": "IH_EY", 922 + "weight": 1.0 923 + }, 924 + { 925 + "a": "IH", 926 + "b": "IH", 927 + "id": "IH_IH", 928 + "weight": 1.0 929 + }, 930 + { 931 + "a": "IH", 932 + "b": "IY", 933 + "id": "IH_IY", 934 + "weight": 1.0 935 + }, 936 + { 937 + "a": "IH", 938 + "b": "OW", 939 + "id": "IH_OW", 940 + "weight": 1.0 941 + }, 942 + { 943 + "a": "IH", 944 + "b": "OY", 945 + "id": "IH_OY", 946 + "weight": 1.0 947 + }, 948 + { 949 + "a": "IH", 950 + "b": "UH", 951 + "id": "IH_UH", 952 + "weight": 1.0 953 + }, 954 + { 955 + "a": "IH", 956 + "b": "UW", 957 + "id": "IH_UW", 958 + "weight": 1.0 959 + }, 960 + { 961 + "a": "IY", 962 + "b": "AA", 963 + "id": "IY_AA", 964 + "weight": 1.0 965 + }, 966 + { 967 + "a": "IY", 968 + "b": "AE", 969 + "id": "IY_AE", 970 + "weight": 1.0 971 + }, 972 + { 973 + "a": "IY", 974 + "b": "AH", 975 + "id": "IY_AH", 976 + "weight": 1.0 977 + }, 978 + { 979 + "a": "IY", 980 + "b": "AO", 981 + "id": "IY_AO", 982 + "weight": 1.0 983 + }, 984 + { 985 + "a": "IY", 986 + "b": "AW", 987 + "id": "IY_AW", 988 + "weight": 1.0 989 + }, 990 + { 991 + "a": "IY", 992 + "b": "AY", 993 + "id": "IY_AY", 994 + "weight": 1.0 995 + }, 996 + { 997 + "a": "IY", 998 + "b": "EH", 999 + "id": "IY_EH", 1000 + "weight": 1.0 1001 + }, 1002 + { 1003 + "a": "IY", 1004 + "b": "ER", 1005 + "id": "IY_ER", 1006 + "weight": 1.0 1007 + }, 1008 + { 1009 + "a": "IY", 1010 + "b": "EY", 1011 + "id": "IY_EY", 1012 + "weight": 1.0 1013 + }, 1014 + { 1015 + "a": "IY", 1016 + "b": "IH", 1017 + "id": "IY_IH", 1018 + "weight": 1.0 1019 + }, 1020 + { 1021 + "a": "IY", 1022 + "b": "IY", 1023 + "id": "IY_IY", 1024 + "weight": 1.0 1025 + }, 1026 + { 1027 + "a": "IY", 1028 + "b": "OW", 1029 + "id": "IY_OW", 1030 + "weight": 1.0 1031 + }, 1032 + { 1033 + "a": "IY", 1034 + "b": "OY", 1035 + "id": "IY_OY", 1036 + "weight": 1.0 1037 + }, 1038 + { 1039 + "a": "IY", 1040 + "b": "UH", 1041 + "id": "IY_UH", 1042 + "weight": 1.0 1043 + }, 1044 + { 1045 + "a": "IY", 1046 + "b": "UW", 1047 + "id": "IY_UW", 1048 + "weight": 1.0 1049 + }, 1050 + { 1051 + "a": "OW", 1052 + "b": "AA", 1053 + "id": "OW_AA", 1054 + "weight": 1.0 1055 + }, 1056 + { 1057 + "a": "OW", 1058 + "b": "AE", 1059 + "id": "OW_AE", 1060 + "weight": 1.0 1061 + }, 1062 + { 1063 + "a": "OW", 1064 + "b": "AH", 1065 + "id": "OW_AH", 1066 + "weight": 1.0 1067 + }, 1068 + { 1069 + "a": "OW", 1070 + "b": "AO", 1071 + "id": "OW_AO", 1072 + "weight": 1.0 1073 + }, 1074 + { 1075 + "a": "OW", 1076 + "b": "AW", 1077 + "id": "OW_AW", 1078 + "weight": 1.0 1079 + }, 1080 + { 1081 + "a": "OW", 1082 + "b": "AY", 1083 + "id": "OW_AY", 1084 + "weight": 1.0 1085 + }, 1086 + { 1087 + "a": "OW", 1088 + "b": "EH", 1089 + "id": "OW_EH", 1090 + "weight": 1.0 1091 + }, 1092 + { 1093 + "a": "OW", 1094 + "b": "ER", 1095 + "id": "OW_ER", 1096 + "weight": 1.0 1097 + }, 1098 + { 1099 + "a": "OW", 1100 + "b": "EY", 1101 + "id": "OW_EY", 1102 + "weight": 1.0 1103 + }, 1104 + { 1105 + "a": "OW", 1106 + "b": "IH", 1107 + "id": "OW_IH", 1108 + "weight": 1.0 1109 + }, 1110 + { 1111 + "a": "OW", 1112 + "b": "IY", 1113 + "id": "OW_IY", 1114 + "weight": 1.0 1115 + }, 1116 + { 1117 + "a": "OW", 1118 + "b": "OW", 1119 + "id": "OW_OW", 1120 + "weight": 1.0 1121 + }, 1122 + { 1123 + "a": "OW", 1124 + "b": "OY", 1125 + "id": "OW_OY", 1126 + "weight": 1.0 1127 + }, 1128 + { 1129 + "a": "OW", 1130 + "b": "UH", 1131 + "id": "OW_UH", 1132 + "weight": 1.0 1133 + }, 1134 + { 1135 + "a": "OW", 1136 + "b": "UW", 1137 + "id": "OW_UW", 1138 + "weight": 1.0 1139 + }, 1140 + { 1141 + "a": "OY", 1142 + "b": "AA", 1143 + "id": "OY_AA", 1144 + "weight": 1.0 1145 + }, 1146 + { 1147 + "a": "OY", 1148 + "b": "AE", 1149 + "id": "OY_AE", 1150 + "weight": 1.0 1151 + }, 1152 + { 1153 + "a": "OY", 1154 + "b": "AH", 1155 + "id": "OY_AH", 1156 + "weight": 1.0 1157 + }, 1158 + { 1159 + "a": "OY", 1160 + "b": "AO", 1161 + "id": "OY_AO", 1162 + "weight": 1.0 1163 + }, 1164 + { 1165 + "a": "OY", 1166 + "b": "AW", 1167 + "id": "OY_AW", 1168 + "weight": 1.0 1169 + }, 1170 + { 1171 + "a": "OY", 1172 + "b": "AY", 1173 + "id": "OY_AY", 1174 + "weight": 1.0 1175 + }, 1176 + { 1177 + "a": "OY", 1178 + "b": "EH", 1179 + "id": "OY_EH", 1180 + "weight": 1.0 1181 + }, 1182 + { 1183 + "a": "OY", 1184 + "b": "ER", 1185 + "id": "OY_ER", 1186 + "weight": 1.0 1187 + }, 1188 + { 1189 + "a": "OY", 1190 + "b": "EY", 1191 + "id": "OY_EY", 1192 + "weight": 1.0 1193 + }, 1194 + { 1195 + "a": "OY", 1196 + "b": "IH", 1197 + "id": "OY_IH", 1198 + "weight": 1.0 1199 + }, 1200 + { 1201 + "a": "OY", 1202 + "b": "IY", 1203 + "id": "OY_IY", 1204 + "weight": 1.0 1205 + }, 1206 + { 1207 + "a": "OY", 1208 + "b": "OW", 1209 + "id": "OY_OW", 1210 + "weight": 1.0 1211 + }, 1212 + { 1213 + "a": "OY", 1214 + "b": "OY", 1215 + "id": "OY_OY", 1216 + "weight": 1.0 1217 + }, 1218 + { 1219 + "a": "OY", 1220 + "b": "UH", 1221 + "id": "OY_UH", 1222 + "weight": 1.0 1223 + }, 1224 + { 1225 + "a": "OY", 1226 + "b": "UW", 1227 + "id": "OY_UW", 1228 + "weight": 1.0 1229 + }, 1230 + { 1231 + "a": "UH", 1232 + "b": "AA", 1233 + "id": "UH_AA", 1234 + "weight": 1.0 1235 + }, 1236 + { 1237 + "a": "UH", 1238 + "b": "AE", 1239 + "id": "UH_AE", 1240 + "weight": 1.0 1241 + }, 1242 + { 1243 + "a": "UH", 1244 + "b": "AH", 1245 + "id": "UH_AH", 1246 + "weight": 1.0 1247 + }, 1248 + { 1249 + "a": "UH", 1250 + "b": "AO", 1251 + "id": "UH_AO", 1252 + "weight": 1.0 1253 + }, 1254 + { 1255 + "a": "UH", 1256 + "b": "AW", 1257 + "id": "UH_AW", 1258 + "weight": 1.0 1259 + }, 1260 + { 1261 + "a": "UH", 1262 + "b": "AY", 1263 + "id": "UH_AY", 1264 + "weight": 1.0 1265 + }, 1266 + { 1267 + "a": "UH", 1268 + "b": "EH", 1269 + "id": "UH_EH", 1270 + "weight": 1.0 1271 + }, 1272 + { 1273 + "a": "UH", 1274 + "b": "ER", 1275 + "id": "UH_ER", 1276 + "weight": 1.0 1277 + }, 1278 + { 1279 + "a": "UH", 1280 + "b": "EY", 1281 + "id": "UH_EY", 1282 + "weight": 1.0 1283 + }, 1284 + { 1285 + "a": "UH", 1286 + "b": "IH", 1287 + "id": "UH_IH", 1288 + "weight": 1.0 1289 + }, 1290 + { 1291 + "a": "UH", 1292 + "b": "IY", 1293 + "id": "UH_IY", 1294 + "weight": 1.0 1295 + }, 1296 + { 1297 + "a": "UH", 1298 + "b": "OW", 1299 + "id": "UH_OW", 1300 + "weight": 1.0 1301 + }, 1302 + { 1303 + "a": "UH", 1304 + "b": "OY", 1305 + "id": "UH_OY", 1306 + "weight": 1.0 1307 + }, 1308 + { 1309 + "a": "UH", 1310 + "b": "UH", 1311 + "id": "UH_UH", 1312 + "weight": 1.0 1313 + }, 1314 + { 1315 + "a": "UH", 1316 + "b": "UW", 1317 + "id": "UH_UW", 1318 + "weight": 1.0 1319 + }, 1320 + { 1321 + "a": "UW", 1322 + "b": "AA", 1323 + "id": "UW_AA", 1324 + "weight": 1.0 1325 + }, 1326 + { 1327 + "a": "UW", 1328 + "b": "AE", 1329 + "id": "UW_AE", 1330 + "weight": 1.0 1331 + }, 1332 + { 1333 + "a": "UW", 1334 + "b": "AH", 1335 + "id": "UW_AH", 1336 + "weight": 1.0 1337 + }, 1338 + { 1339 + "a": "UW", 1340 + "b": "AO", 1341 + "id": "UW_AO", 1342 + "weight": 1.0 1343 + }, 1344 + { 1345 + "a": "UW", 1346 + "b": "AW", 1347 + "id": "UW_AW", 1348 + "weight": 1.0 1349 + }, 1350 + { 1351 + "a": "UW", 1352 + "b": "AY", 1353 + "id": "UW_AY", 1354 + "weight": 1.0 1355 + }, 1356 + { 1357 + "a": "UW", 1358 + "b": "EH", 1359 + "id": "UW_EH", 1360 + "weight": 1.0 1361 + }, 1362 + { 1363 + "a": "UW", 1364 + "b": "ER", 1365 + "id": "UW_ER", 1366 + "weight": 1.0 1367 + }, 1368 + { 1369 + "a": "UW", 1370 + "b": "EY", 1371 + "id": "UW_EY", 1372 + "weight": 1.0 1373 + }, 1374 + { 1375 + "a": "UW", 1376 + "b": "IH", 1377 + "id": "UW_IH", 1378 + "weight": 1.0 1379 + }, 1380 + { 1381 + "a": "UW", 1382 + "b": "IY", 1383 + "id": "UW_IY", 1384 + "weight": 1.0 1385 + }, 1386 + { 1387 + "a": "UW", 1388 + "b": "OW", 1389 + "id": "UW_OW", 1390 + "weight": 1.0 1391 + }, 1392 + { 1393 + "a": "UW", 1394 + "b": "OY", 1395 + "id": "UW_OY", 1396 + "weight": 1.0 1397 + }, 1398 + { 1399 + "a": "UW", 1400 + "b": "UH", 1401 + "id": "UW_UH", 1402 + "weight": 1.0 1403 + }, 1404 + { 1405 + "a": "UW", 1406 + "b": "UW", 1407 + "id": "UW_UW", 1408 + "weight": 1.0 1409 + }, 1410 + { 1411 + "a": "#B", 1412 + "b": "AA", 1413 + "id": "#B_AA", 1414 + "weight": 0.95 1415 + }, 1416 + { 1417 + "a": "#B", 1418 + "b": "AE", 1419 + "id": "#B_AE", 1420 + "weight": 0.95 1421 + }, 1422 + { 1423 + "a": "#B", 1424 + "b": "AH", 1425 + "id": "#B_AH", 1426 + "weight": 0.95 1427 + }, 1428 + { 1429 + "a": "#B", 1430 + "b": "AO", 1431 + "id": "#B_AO", 1432 + "weight": 0.95 1433 + }, 1434 + { 1435 + "a": "#B", 1436 + "b": "AW", 1437 + "id": "#B_AW", 1438 + "weight": 0.95 1439 + }, 1440 + { 1441 + "a": "#B", 1442 + "b": "AY", 1443 + "id": "#B_AY", 1444 + "weight": 0.95 1445 + }, 1446 + { 1447 + "a": "#B", 1448 + "b": "EH", 1449 + "id": "#B_EH", 1450 + "weight": 0.95 1451 + }, 1452 + { 1453 + "a": "#B", 1454 + "b": "ER", 1455 + "id": "#B_ER", 1456 + "weight": 0.95 1457 + }, 1458 + { 1459 + "a": "#B", 1460 + "b": "EY", 1461 + "id": "#B_EY", 1462 + "weight": 0.95 1463 + }, 1464 + { 1465 + "a": "#B", 1466 + "b": "IH", 1467 + "id": "#B_IH", 1468 + "weight": 0.95 1469 + }, 1470 + { 1471 + "a": "#B", 1472 + "b": "IY", 1473 + "id": "#B_IY", 1474 + "weight": 0.95 1475 + }, 1476 + { 1477 + "a": "#B", 1478 + "b": "OW", 1479 + "id": "#B_OW", 1480 + "weight": 0.95 1481 + }, 1482 + { 1483 + "a": "#B", 1484 + "b": "OY", 1485 + "id": "#B_OY", 1486 + "weight": 0.95 1487 + }, 1488 + { 1489 + "a": "#B", 1490 + "b": "UH", 1491 + "id": "#B_UH", 1492 + "weight": 0.95 1493 + }, 1494 + { 1495 + "a": "#B", 1496 + "b": "UW", 1497 + "id": "#B_UW", 1498 + "weight": 0.95 1499 + }, 1500 + { 1501 + "a": "B", 1502 + "b": "AA", 1503 + "id": "B_AA", 1504 + "weight": 0.95 1505 + }, 1506 + { 1507 + "a": "B", 1508 + "b": "AE", 1509 + "id": "B_AE", 1510 + "weight": 0.95 1511 + }, 1512 + { 1513 + "a": "B", 1514 + "b": "AH", 1515 + "id": "B_AH", 1516 + "weight": 0.95 1517 + }, 1518 + { 1519 + "a": "B", 1520 + "b": "AO", 1521 + "id": "B_AO", 1522 + "weight": 0.95 1523 + }, 1524 + { 1525 + "a": "B", 1526 + "b": "AW", 1527 + "id": "B_AW", 1528 + "weight": 0.95 1529 + }, 1530 + { 1531 + "a": "B", 1532 + "b": "AY", 1533 + "id": "B_AY", 1534 + "weight": 0.95 1535 + }, 1536 + { 1537 + "a": "B", 1538 + "b": "EH", 1539 + "id": "B_EH", 1540 + "weight": 0.95 1541 + }, 1542 + { 1543 + "a": "B", 1544 + "b": "ER", 1545 + "id": "B_ER", 1546 + "weight": 0.95 1547 + }, 1548 + { 1549 + "a": "B", 1550 + "b": "EY", 1551 + "id": "B_EY", 1552 + "weight": 0.95 1553 + }, 1554 + { 1555 + "a": "B", 1556 + "b": "IH", 1557 + "id": "B_IH", 1558 + "weight": 0.95 1559 + }, 1560 + { 1561 + "a": "B", 1562 + "b": "IY", 1563 + "id": "B_IY", 1564 + "weight": 0.95 1565 + }, 1566 + { 1567 + "a": "B", 1568 + "b": "OW", 1569 + "id": "B_OW", 1570 + "weight": 0.95 1571 + }, 1572 + { 1573 + "a": "B", 1574 + "b": "OY", 1575 + "id": "B_OY", 1576 + "weight": 0.95 1577 + }, 1578 + { 1579 + "a": "B", 1580 + "b": "UH", 1581 + "id": "B_UH", 1582 + "weight": 0.95 1583 + }, 1584 + { 1585 + "a": "B", 1586 + "b": "UW", 1587 + "id": "B_UW", 1588 + "weight": 0.95 1589 + }, 1590 + { 1591 + "a": "D", 1592 + "b": "AA", 1593 + "id": "D_AA", 1594 + "weight": 0.95 1595 + }, 1596 + { 1597 + "a": "D", 1598 + "b": "AE", 1599 + "id": "D_AE", 1600 + "weight": 0.95 1601 + }, 1602 + { 1603 + "a": "D", 1604 + "b": "AH", 1605 + "id": "D_AH", 1606 + "weight": 0.95 1607 + }, 1608 + { 1609 + "a": "D", 1610 + "b": "AO", 1611 + "id": "D_AO", 1612 + "weight": 0.95 1613 + }, 1614 + { 1615 + "a": "D", 1616 + "b": "AW", 1617 + "id": "D_AW", 1618 + "weight": 0.95 1619 + }, 1620 + { 1621 + "a": "D", 1622 + "b": "AY", 1623 + "id": "D_AY", 1624 + "weight": 0.95 1625 + }, 1626 + { 1627 + "a": "D", 1628 + "b": "EH", 1629 + "id": "D_EH", 1630 + "weight": 0.95 1631 + }, 1632 + { 1633 + "a": "D", 1634 + "b": "ER", 1635 + "id": "D_ER", 1636 + "weight": 0.95 1637 + }, 1638 + { 1639 + "a": "D", 1640 + "b": "EY", 1641 + "id": "D_EY", 1642 + "weight": 0.95 1643 + }, 1644 + { 1645 + "a": "D", 1646 + "b": "IH", 1647 + "id": "D_IH", 1648 + "weight": 0.95 1649 + }, 1650 + { 1651 + "a": "D", 1652 + "b": "IY", 1653 + "id": "D_IY", 1654 + "weight": 0.95 1655 + }, 1656 + { 1657 + "a": "D", 1658 + "b": "OW", 1659 + "id": "D_OW", 1660 + "weight": 0.95 1661 + }, 1662 + { 1663 + "a": "D", 1664 + "b": "OY", 1665 + "id": "D_OY", 1666 + "weight": 0.95 1667 + }, 1668 + { 1669 + "a": "D", 1670 + "b": "UH", 1671 + "id": "D_UH", 1672 + "weight": 0.95 1673 + }, 1674 + { 1675 + "a": "D", 1676 + "b": "UW", 1677 + "id": "D_UW", 1678 + "weight": 0.95 1679 + }, 1680 + { 1681 + "a": "G", 1682 + "b": "AA", 1683 + "id": "G_AA", 1684 + "weight": 0.95 1685 + }, 1686 + { 1687 + "a": "G", 1688 + "b": "AE", 1689 + "id": "G_AE", 1690 + "weight": 0.95 1691 + }, 1692 + { 1693 + "a": "G", 1694 + "b": "AH", 1695 + "id": "G_AH", 1696 + "weight": 0.95 1697 + }, 1698 + { 1699 + "a": "G", 1700 + "b": "AO", 1701 + "id": "G_AO", 1702 + "weight": 0.95 1703 + }, 1704 + { 1705 + "a": "G", 1706 + "b": "AW", 1707 + "id": "G_AW", 1708 + "weight": 0.95 1709 + }, 1710 + { 1711 + "a": "G", 1712 + "b": "AY", 1713 + "id": "G_AY", 1714 + "weight": 0.95 1715 + }, 1716 + { 1717 + "a": "G", 1718 + "b": "EH", 1719 + "id": "G_EH", 1720 + "weight": 0.95 1721 + }, 1722 + { 1723 + "a": "G", 1724 + "b": "ER", 1725 + "id": "G_ER", 1726 + "weight": 0.95 1727 + }, 1728 + { 1729 + "a": "G", 1730 + "b": "EY", 1731 + "id": "G_EY", 1732 + "weight": 0.95 1733 + }, 1734 + { 1735 + "a": "G", 1736 + "b": "IH", 1737 + "id": "G_IH", 1738 + "weight": 0.95 1739 + }, 1740 + { 1741 + "a": "G", 1742 + "b": "IY", 1743 + "id": "G_IY", 1744 + "weight": 0.95 1745 + }, 1746 + { 1747 + "a": "G", 1748 + "b": "OW", 1749 + "id": "G_OW", 1750 + "weight": 0.95 1751 + }, 1752 + { 1753 + "a": "G", 1754 + "b": "OY", 1755 + "id": "G_OY", 1756 + "weight": 0.95 1757 + }, 1758 + { 1759 + "a": "G", 1760 + "b": "UH", 1761 + "id": "G_UH", 1762 + "weight": 0.95 1763 + }, 1764 + { 1765 + "a": "G", 1766 + "b": "UW", 1767 + "id": "G_UW", 1768 + "weight": 0.95 1769 + }, 1770 + { 1771 + "a": "K", 1772 + "b": "AA", 1773 + "id": "K_AA", 1774 + "weight": 0.95 1775 + }, 1776 + { 1777 + "a": "K", 1778 + "b": "AE", 1779 + "id": "K_AE", 1780 + "weight": 0.95 1781 + }, 1782 + { 1783 + "a": "K", 1784 + "b": "AH", 1785 + "id": "K_AH", 1786 + "weight": 0.95 1787 + }, 1788 + { 1789 + "a": "K", 1790 + "b": "AO", 1791 + "id": "K_AO", 1792 + "weight": 0.95 1793 + }, 1794 + { 1795 + "a": "K", 1796 + "b": "AW", 1797 + "id": "K_AW", 1798 + "weight": 0.95 1799 + }, 1800 + { 1801 + "a": "K", 1802 + "b": "AY", 1803 + "id": "K_AY", 1804 + "weight": 0.95 1805 + }, 1806 + { 1807 + "a": "K", 1808 + "b": "EH", 1809 + "id": "K_EH", 1810 + "weight": 0.95 1811 + }, 1812 + { 1813 + "a": "K", 1814 + "b": "ER", 1815 + "id": "K_ER", 1816 + "weight": 0.95 1817 + }, 1818 + { 1819 + "a": "K", 1820 + "b": "EY", 1821 + "id": "K_EY", 1822 + "weight": 0.95 1823 + }, 1824 + { 1825 + "a": "K", 1826 + "b": "IH", 1827 + "id": "K_IH", 1828 + "weight": 0.95 1829 + }, 1830 + { 1831 + "a": "K", 1832 + "b": "IY", 1833 + "id": "K_IY", 1834 + "weight": 0.95 1835 + }, 1836 + { 1837 + "a": "K", 1838 + "b": "OW", 1839 + "id": "K_OW", 1840 + "weight": 0.95 1841 + }, 1842 + { 1843 + "a": "K", 1844 + "b": "OY", 1845 + "id": "K_OY", 1846 + "weight": 0.95 1847 + }, 1848 + { 1849 + "a": "K", 1850 + "b": "UH", 1851 + "id": "K_UH", 1852 + "weight": 0.95 1853 + }, 1854 + { 1855 + "a": "K", 1856 + "b": "UW", 1857 + "id": "K_UW", 1858 + "weight": 0.95 1859 + }, 1860 + { 1861 + "a": "P", 1862 + "b": "AA", 1863 + "id": "P_AA", 1864 + "weight": 0.95 1865 + }, 1866 + { 1867 + "a": "P", 1868 + "b": "AE", 1869 + "id": "P_AE", 1870 + "weight": 0.95 1871 + }, 1872 + { 1873 + "a": "P", 1874 + "b": "AH", 1875 + "id": "P_AH", 1876 + "weight": 0.95 1877 + }, 1878 + { 1879 + "a": "P", 1880 + "b": "AO", 1881 + "id": "P_AO", 1882 + "weight": 0.95 1883 + }, 1884 + { 1885 + "a": "P", 1886 + "b": "AW", 1887 + "id": "P_AW", 1888 + "weight": 0.95 1889 + }, 1890 + { 1891 + "a": "P", 1892 + "b": "AY", 1893 + "id": "P_AY", 1894 + "weight": 0.95 1895 + }, 1896 + { 1897 + "a": "P", 1898 + "b": "EH", 1899 + "id": "P_EH", 1900 + "weight": 0.95 1901 + }, 1902 + { 1903 + "a": "P", 1904 + "b": "ER", 1905 + "id": "P_ER", 1906 + "weight": 0.95 1907 + }, 1908 + { 1909 + "a": "P", 1910 + "b": "EY", 1911 + "id": "P_EY", 1912 + "weight": 0.95 1913 + }, 1914 + { 1915 + "a": "P", 1916 + "b": "IH", 1917 + "id": "P_IH", 1918 + "weight": 0.95 1919 + }, 1920 + { 1921 + "a": "P", 1922 + "b": "IY", 1923 + "id": "P_IY", 1924 + "weight": 0.95 1925 + }, 1926 + { 1927 + "a": "P", 1928 + "b": "OW", 1929 + "id": "P_OW", 1930 + "weight": 0.95 1931 + }, 1932 + { 1933 + "a": "P", 1934 + "b": "OY", 1935 + "id": "P_OY", 1936 + "weight": 0.95 1937 + }, 1938 + { 1939 + "a": "P", 1940 + "b": "UH", 1941 + "id": "P_UH", 1942 + "weight": 0.95 1943 + }, 1944 + { 1945 + "a": "P", 1946 + "b": "UW", 1947 + "id": "P_UW", 1948 + "weight": 0.95 1949 + }, 1950 + { 1951 + "a": "T", 1952 + "b": "AA", 1953 + "id": "T_AA", 1954 + "weight": 0.95 1955 + }, 1956 + { 1957 + "a": "T", 1958 + "b": "AE", 1959 + "id": "T_AE", 1960 + "weight": 0.95 1961 + }, 1962 + { 1963 + "a": "T", 1964 + "b": "AH", 1965 + "id": "T_AH", 1966 + "weight": 0.95 1967 + }, 1968 + { 1969 + "a": "T", 1970 + "b": "AO", 1971 + "id": "T_AO", 1972 + "weight": 0.95 1973 + }, 1974 + { 1975 + "a": "T", 1976 + "b": "AW", 1977 + "id": "T_AW", 1978 + "weight": 0.95 1979 + }, 1980 + { 1981 + "a": "T", 1982 + "b": "AY", 1983 + "id": "T_AY", 1984 + "weight": 0.95 1985 + }, 1986 + { 1987 + "a": "T", 1988 + "b": "EH", 1989 + "id": "T_EH", 1990 + "weight": 0.95 1991 + }, 1992 + { 1993 + "a": "T", 1994 + "b": "ER", 1995 + "id": "T_ER", 1996 + "weight": 0.95 1997 + }, 1998 + { 1999 + "a": "T", 2000 + "b": "EY", 2001 + "id": "T_EY", 2002 + "weight": 0.95 2003 + }, 2004 + { 2005 + "a": "T", 2006 + "b": "IH", 2007 + "id": "T_IH", 2008 + "weight": 0.95 2009 + }, 2010 + { 2011 + "a": "T", 2012 + "b": "IY", 2013 + "id": "T_IY", 2014 + "weight": 0.95 2015 + }, 2016 + { 2017 + "a": "T", 2018 + "b": "OW", 2019 + "id": "T_OW", 2020 + "weight": 0.95 2021 + }, 2022 + { 2023 + "a": "T", 2024 + "b": "OY", 2025 + "id": "T_OY", 2026 + "weight": 0.95 2027 + }, 2028 + { 2029 + "a": "T", 2030 + "b": "UH", 2031 + "id": "T_UH", 2032 + "weight": 0.95 2033 + }, 2034 + { 2035 + "a": "T", 2036 + "b": "UW", 2037 + "id": "T_UW", 2038 + "weight": 0.95 2039 + }, 2040 + { 2041 + "a": "AA", 2042 + "b": "B", 2043 + "id": "AA_B", 2044 + "weight": 0.9 2045 + }, 2046 + { 2047 + "a": "AA", 2048 + "b": "D", 2049 + "id": "AA_D", 2050 + "weight": 0.9 2051 + }, 2052 + { 2053 + "a": "AA", 2054 + "b": "G", 2055 + "id": "AA_G", 2056 + "weight": 0.9 2057 + }, 2058 + { 2059 + "a": "AA", 2060 + "b": "K", 2061 + "id": "AA_K", 2062 + "weight": 0.9 2063 + }, 2064 + { 2065 + "a": "AA", 2066 + "b": "P", 2067 + "id": "AA_P", 2068 + "weight": 0.9 2069 + }, 2070 + { 2071 + "a": "AA", 2072 + "b": "T", 2073 + "id": "AA_T", 2074 + "weight": 0.9 2075 + }, 2076 + { 2077 + "a": "AE", 2078 + "b": "B", 2079 + "id": "AE_B", 2080 + "weight": 0.9 2081 + }, 2082 + { 2083 + "a": "AE", 2084 + "b": "D", 2085 + "id": "AE_D", 2086 + "weight": 0.9 2087 + }, 2088 + { 2089 + "a": "AE", 2090 + "b": "G", 2091 + "id": "AE_G", 2092 + "weight": 0.9 2093 + }, 2094 + { 2095 + "a": "AE", 2096 + "b": "K", 2097 + "id": "AE_K", 2098 + "weight": 0.9 2099 + }, 2100 + { 2101 + "a": "AE", 2102 + "b": "P", 2103 + "id": "AE_P", 2104 + "weight": 0.9 2105 + }, 2106 + { 2107 + "a": "AE", 2108 + "b": "T", 2109 + "id": "AE_T", 2110 + "weight": 0.9 2111 + }, 2112 + { 2113 + "a": "AH", 2114 + "b": "B", 2115 + "id": "AH_B", 2116 + "weight": 0.9 2117 + }, 2118 + { 2119 + "a": "AH", 2120 + "b": "D", 2121 + "id": "AH_D", 2122 + "weight": 0.9 2123 + }, 2124 + { 2125 + "a": "AH", 2126 + "b": "G", 2127 + "id": "AH_G", 2128 + "weight": 0.9 2129 + }, 2130 + { 2131 + "a": "AH", 2132 + "b": "K", 2133 + "id": "AH_K", 2134 + "weight": 0.9 2135 + }, 2136 + { 2137 + "a": "AH", 2138 + "b": "P", 2139 + "id": "AH_P", 2140 + "weight": 0.9 2141 + }, 2142 + { 2143 + "a": "AH", 2144 + "b": "T", 2145 + "id": "AH_T", 2146 + "weight": 0.9 2147 + }, 2148 + { 2149 + "a": "AO", 2150 + "b": "B", 2151 + "id": "AO_B", 2152 + "weight": 0.9 2153 + }, 2154 + { 2155 + "a": "AO", 2156 + "b": "D", 2157 + "id": "AO_D", 2158 + "weight": 0.9 2159 + }, 2160 + { 2161 + "a": "AO", 2162 + "b": "G", 2163 + "id": "AO_G", 2164 + "weight": 0.9 2165 + }, 2166 + { 2167 + "a": "AO", 2168 + "b": "K", 2169 + "id": "AO_K", 2170 + "weight": 0.9 2171 + }, 2172 + { 2173 + "a": "AO", 2174 + "b": "P", 2175 + "id": "AO_P", 2176 + "weight": 0.9 2177 + }, 2178 + { 2179 + "a": "AO", 2180 + "b": "T", 2181 + "id": "AO_T", 2182 + "weight": 0.9 2183 + }, 2184 + { 2185 + "a": "AW", 2186 + "b": "B", 2187 + "id": "AW_B", 2188 + "weight": 0.9 2189 + }, 2190 + { 2191 + "a": "AW", 2192 + "b": "D", 2193 + "id": "AW_D", 2194 + "weight": 0.9 2195 + }, 2196 + { 2197 + "a": "AW", 2198 + "b": "G", 2199 + "id": "AW_G", 2200 + "weight": 0.9 2201 + }, 2202 + { 2203 + "a": "AW", 2204 + "b": "K", 2205 + "id": "AW_K", 2206 + "weight": 0.9 2207 + }, 2208 + { 2209 + "a": "AW", 2210 + "b": "P", 2211 + "id": "AW_P", 2212 + "weight": 0.9 2213 + }, 2214 + { 2215 + "a": "AW", 2216 + "b": "T", 2217 + "id": "AW_T", 2218 + "weight": 0.9 2219 + }, 2220 + { 2221 + "a": "AY", 2222 + "b": "B", 2223 + "id": "AY_B", 2224 + "weight": 0.9 2225 + }, 2226 + { 2227 + "a": "AY", 2228 + "b": "D", 2229 + "id": "AY_D", 2230 + "weight": 0.9 2231 + }, 2232 + { 2233 + "a": "AY", 2234 + "b": "G", 2235 + "id": "AY_G", 2236 + "weight": 0.9 2237 + }, 2238 + { 2239 + "a": "AY", 2240 + "b": "K", 2241 + "id": "AY_K", 2242 + "weight": 0.9 2243 + }, 2244 + { 2245 + "a": "AY", 2246 + "b": "P", 2247 + "id": "AY_P", 2248 + "weight": 0.9 2249 + }, 2250 + { 2251 + "a": "AY", 2252 + "b": "T", 2253 + "id": "AY_T", 2254 + "weight": 0.9 2255 + }, 2256 + { 2257 + "a": "EH", 2258 + "b": "B", 2259 + "id": "EH_B", 2260 + "weight": 0.9 2261 + }, 2262 + { 2263 + "a": "EH", 2264 + "b": "D", 2265 + "id": "EH_D", 2266 + "weight": 0.9 2267 + }, 2268 + { 2269 + "a": "EH", 2270 + "b": "G", 2271 + "id": "EH_G", 2272 + "weight": 0.9 2273 + }, 2274 + { 2275 + "a": "EH", 2276 + "b": "K", 2277 + "id": "EH_K", 2278 + "weight": 0.9 2279 + }, 2280 + { 2281 + "a": "EH", 2282 + "b": "P", 2283 + "id": "EH_P", 2284 + "weight": 0.9 2285 + }, 2286 + { 2287 + "a": "EH", 2288 + "b": "T", 2289 + "id": "EH_T", 2290 + "weight": 0.9 2291 + }, 2292 + { 2293 + "a": "ER", 2294 + "b": "B", 2295 + "id": "ER_B", 2296 + "weight": 0.9 2297 + }, 2298 + { 2299 + "a": "ER", 2300 + "b": "D", 2301 + "id": "ER_D", 2302 + "weight": 0.9 2303 + }, 2304 + { 2305 + "a": "ER", 2306 + "b": "G", 2307 + "id": "ER_G", 2308 + "weight": 0.9 2309 + }, 2310 + { 2311 + "a": "ER", 2312 + "b": "K", 2313 + "id": "ER_K", 2314 + "weight": 0.9 2315 + }, 2316 + { 2317 + "a": "ER", 2318 + "b": "P", 2319 + "id": "ER_P", 2320 + "weight": 0.9 2321 + }, 2322 + { 2323 + "a": "ER", 2324 + "b": "T", 2325 + "id": "ER_T", 2326 + "weight": 0.9 2327 + }, 2328 + { 2329 + "a": "EY", 2330 + "b": "B", 2331 + "id": "EY_B", 2332 + "weight": 0.9 2333 + }, 2334 + { 2335 + "a": "EY", 2336 + "b": "D", 2337 + "id": "EY_D", 2338 + "weight": 0.9 2339 + }, 2340 + { 2341 + "a": "EY", 2342 + "b": "G", 2343 + "id": "EY_G", 2344 + "weight": 0.9 2345 + }, 2346 + { 2347 + "a": "EY", 2348 + "b": "K", 2349 + "id": "EY_K", 2350 + "weight": 0.9 2351 + }, 2352 + { 2353 + "a": "EY", 2354 + "b": "P", 2355 + "id": "EY_P", 2356 + "weight": 0.9 2357 + }, 2358 + { 2359 + "a": "EY", 2360 + "b": "T", 2361 + "id": "EY_T", 2362 + "weight": 0.9 2363 + }, 2364 + { 2365 + "a": "IH", 2366 + "b": "B", 2367 + "id": "IH_B", 2368 + "weight": 0.9 2369 + }, 2370 + { 2371 + "a": "IH", 2372 + "b": "D", 2373 + "id": "IH_D", 2374 + "weight": 0.9 2375 + }, 2376 + { 2377 + "a": "IH", 2378 + "b": "G", 2379 + "id": "IH_G", 2380 + "weight": 0.9 2381 + }, 2382 + { 2383 + "a": "IH", 2384 + "b": "K", 2385 + "id": "IH_K", 2386 + "weight": 0.9 2387 + }, 2388 + { 2389 + "a": "IH", 2390 + "b": "P", 2391 + "id": "IH_P", 2392 + "weight": 0.9 2393 + }, 2394 + { 2395 + "a": "IH", 2396 + "b": "T", 2397 + "id": "IH_T", 2398 + "weight": 0.9 2399 + }, 2400 + { 2401 + "a": "IY", 2402 + "b": "B", 2403 + "id": "IY_B", 2404 + "weight": 0.9 2405 + }, 2406 + { 2407 + "a": "IY", 2408 + "b": "D", 2409 + "id": "IY_D", 2410 + "weight": 0.9 2411 + }, 2412 + { 2413 + "a": "IY", 2414 + "b": "G", 2415 + "id": "IY_G", 2416 + "weight": 0.9 2417 + }, 2418 + { 2419 + "a": "IY", 2420 + "b": "K", 2421 + "id": "IY_K", 2422 + "weight": 0.9 2423 + }, 2424 + { 2425 + "a": "IY", 2426 + "b": "P", 2427 + "id": "IY_P", 2428 + "weight": 0.9 2429 + }, 2430 + { 2431 + "a": "IY", 2432 + "b": "T", 2433 + "id": "IY_T", 2434 + "weight": 0.9 2435 + }, 2436 + { 2437 + "a": "OW", 2438 + "b": "B", 2439 + "id": "OW_B", 2440 + "weight": 0.9 2441 + }, 2442 + { 2443 + "a": "OW", 2444 + "b": "D", 2445 + "id": "OW_D", 2446 + "weight": 0.9 2447 + }, 2448 + { 2449 + "a": "OW", 2450 + "b": "G", 2451 + "id": "OW_G", 2452 + "weight": 0.9 2453 + }, 2454 + { 2455 + "a": "OW", 2456 + "b": "K", 2457 + "id": "OW_K", 2458 + "weight": 0.9 2459 + }, 2460 + { 2461 + "a": "OW", 2462 + "b": "P", 2463 + "id": "OW_P", 2464 + "weight": 0.9 2465 + }, 2466 + { 2467 + "a": "OW", 2468 + "b": "T", 2469 + "id": "OW_T", 2470 + "weight": 0.9 2471 + }, 2472 + { 2473 + "a": "OY", 2474 + "b": "B", 2475 + "id": "OY_B", 2476 + "weight": 0.9 2477 + }, 2478 + { 2479 + "a": "OY", 2480 + "b": "D", 2481 + "id": "OY_D", 2482 + "weight": 0.9 2483 + }, 2484 + { 2485 + "a": "OY", 2486 + "b": "G", 2487 + "id": "OY_G", 2488 + "weight": 0.9 2489 + }, 2490 + { 2491 + "a": "OY", 2492 + "b": "K", 2493 + "id": "OY_K", 2494 + "weight": 0.9 2495 + }, 2496 + { 2497 + "a": "OY", 2498 + "b": "P", 2499 + "id": "OY_P", 2500 + "weight": 0.9 2501 + }, 2502 + { 2503 + "a": "OY", 2504 + "b": "T", 2505 + "id": "OY_T", 2506 + "weight": 0.9 2507 + }, 2508 + { 2509 + "a": "UH", 2510 + "b": "B", 2511 + "id": "UH_B", 2512 + "weight": 0.9 2513 + }, 2514 + { 2515 + "a": "UH", 2516 + "b": "D", 2517 + "id": "UH_D", 2518 + "weight": 0.9 2519 + }, 2520 + { 2521 + "a": "UH", 2522 + "b": "G", 2523 + "id": "UH_G", 2524 + "weight": 0.9 2525 + }, 2526 + { 2527 + "a": "UH", 2528 + "b": "K", 2529 + "id": "UH_K", 2530 + "weight": 0.9 2531 + }, 2532 + { 2533 + "a": "UH", 2534 + "b": "P", 2535 + "id": "UH_P", 2536 + "weight": 0.9 2537 + }, 2538 + { 2539 + "a": "UH", 2540 + "b": "T", 2541 + "id": "UH_T", 2542 + "weight": 0.9 2543 + }, 2544 + { 2545 + "a": "UW", 2546 + "b": "B", 2547 + "id": "UW_B", 2548 + "weight": 0.9 2549 + }, 2550 + { 2551 + "a": "UW", 2552 + "b": "D", 2553 + "id": "UW_D", 2554 + "weight": 0.9 2555 + }, 2556 + { 2557 + "a": "UW", 2558 + "b": "G", 2559 + "id": "UW_G", 2560 + "weight": 0.9 2561 + }, 2562 + { 2563 + "a": "UW", 2564 + "b": "K", 2565 + "id": "UW_K", 2566 + "weight": 0.9 2567 + }, 2568 + { 2569 + "a": "UW", 2570 + "b": "P", 2571 + "id": "UW_P", 2572 + "weight": 0.9 2573 + }, 2574 + { 2575 + "a": "UW", 2576 + "b": "T", 2577 + "id": "UW_T", 2578 + "weight": 0.9 2579 + }, 2580 + { 2581 + "a": "#B", 2582 + "b": "B", 2583 + "id": "#B_B", 2584 + "weight": 0.85 2585 + }, 2586 + { 2587 + "a": "#B", 2588 + "b": "CH", 2589 + "id": "#B_CH", 2590 + "weight": 0.85 2591 + }, 2592 + { 2593 + "a": "#B", 2594 + "b": "D", 2595 + "id": "#B_D", 2596 + "weight": 0.85 2597 + }, 2598 + { 2599 + "a": "#B", 2600 + "b": "DH", 2601 + "id": "#B_DH", 2602 + "weight": 0.85 2603 + }, 2604 + { 2605 + "a": "#B", 2606 + "b": "F", 2607 + "id": "#B_F", 2608 + "weight": 0.85 2609 + }, 2610 + { 2611 + "a": "#B", 2612 + "b": "G", 2613 + "id": "#B_G", 2614 + "weight": 0.85 2615 + }, 2616 + { 2617 + "a": "#B", 2618 + "b": "HH", 2619 + "id": "#B_HH", 2620 + "weight": 0.85 2621 + }, 2622 + { 2623 + "a": "#B", 2624 + "b": "JH", 2625 + "id": "#B_JH", 2626 + "weight": 0.85 2627 + }, 2628 + { 2629 + "a": "#B", 2630 + "b": "K", 2631 + "id": "#B_K", 2632 + "weight": 0.85 2633 + }, 2634 + { 2635 + "a": "#B", 2636 + "b": "L", 2637 + "id": "#B_L", 2638 + "weight": 0.85 2639 + }, 2640 + { 2641 + "a": "#B", 2642 + "b": "M", 2643 + "id": "#B_M", 2644 + "weight": 0.85 2645 + }, 2646 + { 2647 + "a": "#B", 2648 + "b": "N", 2649 + "id": "#B_N", 2650 + "weight": 0.85 2651 + }, 2652 + { 2653 + "a": "#B", 2654 + "b": "P", 2655 + "id": "#B_P", 2656 + "weight": 0.85 2657 + }, 2658 + { 2659 + "a": "#B", 2660 + "b": "R", 2661 + "id": "#B_R", 2662 + "weight": 0.85 2663 + }, 2664 + { 2665 + "a": "#B", 2666 + "b": "S", 2667 + "id": "#B_S", 2668 + "weight": 0.85 2669 + }, 2670 + { 2671 + "a": "#B", 2672 + "b": "SH", 2673 + "id": "#B_SH", 2674 + "weight": 0.85 2675 + }, 2676 + { 2677 + "a": "#B", 2678 + "b": "T", 2679 + "id": "#B_T", 2680 + "weight": 0.85 2681 + }, 2682 + { 2683 + "a": "#B", 2684 + "b": "TH", 2685 + "id": "#B_TH", 2686 + "weight": 0.85 2687 + }, 2688 + { 2689 + "a": "#B", 2690 + "b": "V", 2691 + "id": "#B_V", 2692 + "weight": 0.85 2693 + }, 2694 + { 2695 + "a": "#B", 2696 + "b": "W", 2697 + "id": "#B_W", 2698 + "weight": 0.85 2699 + }, 2700 + { 2701 + "a": "#B", 2702 + "b": "Y", 2703 + "id": "#B_Y", 2704 + "weight": 0.85 2705 + }, 2706 + { 2707 + "a": "#B", 2708 + "b": "Z", 2709 + "id": "#B_Z", 2710 + "weight": 0.85 2711 + }, 2712 + { 2713 + "a": "AA", 2714 + "b": "M", 2715 + "id": "AA_M", 2716 + "weight": 0.85 2717 + }, 2718 + { 2719 + "a": "AA", 2720 + "b": "N", 2721 + "id": "AA_N", 2722 + "weight": 0.85 2723 + }, 2724 + { 2725 + "a": "AA", 2726 + "b": "NG", 2727 + "id": "AA_NG", 2728 + "weight": 0.85 2729 + }, 2730 + { 2731 + "a": "AE", 2732 + "b": "M", 2733 + "id": "AE_M", 2734 + "weight": 0.85 2735 + }, 2736 + { 2737 + "a": "AE", 2738 + "b": "N", 2739 + "id": "AE_N", 2740 + "weight": 0.85 2741 + }, 2742 + { 2743 + "a": "AE", 2744 + "b": "NG", 2745 + "id": "AE_NG", 2746 + "weight": 0.85 2747 + }, 2748 + { 2749 + "a": "AH", 2750 + "b": "M", 2751 + "id": "AH_M", 2752 + "weight": 0.85 2753 + }, 2754 + { 2755 + "a": "AH", 2756 + "b": "N", 2757 + "id": "AH_N", 2758 + "weight": 0.85 2759 + }, 2760 + { 2761 + "a": "AH", 2762 + "b": "NG", 2763 + "id": "AH_NG", 2764 + "weight": 0.85 2765 + }, 2766 + { 2767 + "a": "AO", 2768 + "b": "M", 2769 + "id": "AO_M", 2770 + "weight": 0.85 2771 + }, 2772 + { 2773 + "a": "AO", 2774 + "b": "N", 2775 + "id": "AO_N", 2776 + "weight": 0.85 2777 + }, 2778 + { 2779 + "a": "AO", 2780 + "b": "NG", 2781 + "id": "AO_NG", 2782 + "weight": 0.85 2783 + }, 2784 + { 2785 + "a": "AW", 2786 + "b": "M", 2787 + "id": "AW_M", 2788 + "weight": 0.85 2789 + }, 2790 + { 2791 + "a": "AW", 2792 + "b": "N", 2793 + "id": "AW_N", 2794 + "weight": 0.85 2795 + }, 2796 + { 2797 + "a": "AW", 2798 + "b": "NG", 2799 + "id": "AW_NG", 2800 + "weight": 0.85 2801 + }, 2802 + { 2803 + "a": "AY", 2804 + "b": "M", 2805 + "id": "AY_M", 2806 + "weight": 0.85 2807 + }, 2808 + { 2809 + "a": "AY", 2810 + "b": "N", 2811 + "id": "AY_N", 2812 + "weight": 0.85 2813 + }, 2814 + { 2815 + "a": "AY", 2816 + "b": "NG", 2817 + "id": "AY_NG", 2818 + "weight": 0.85 2819 + }, 2820 + { 2821 + "a": "EH", 2822 + "b": "M", 2823 + "id": "EH_M", 2824 + "weight": 0.85 2825 + }, 2826 + { 2827 + "a": "EH", 2828 + "b": "N", 2829 + "id": "EH_N", 2830 + "weight": 0.85 2831 + }, 2832 + { 2833 + "a": "EH", 2834 + "b": "NG", 2835 + "id": "EH_NG", 2836 + "weight": 0.85 2837 + }, 2838 + { 2839 + "a": "ER", 2840 + "b": "M", 2841 + "id": "ER_M", 2842 + "weight": 0.85 2843 + }, 2844 + { 2845 + "a": "ER", 2846 + "b": "N", 2847 + "id": "ER_N", 2848 + "weight": 0.85 2849 + }, 2850 + { 2851 + "a": "ER", 2852 + "b": "NG", 2853 + "id": "ER_NG", 2854 + "weight": 0.85 2855 + }, 2856 + { 2857 + "a": "EY", 2858 + "b": "M", 2859 + "id": "EY_M", 2860 + "weight": 0.85 2861 + }, 2862 + { 2863 + "a": "EY", 2864 + "b": "N", 2865 + "id": "EY_N", 2866 + "weight": 0.85 2867 + }, 2868 + { 2869 + "a": "EY", 2870 + "b": "NG", 2871 + "id": "EY_NG", 2872 + "weight": 0.85 2873 + }, 2874 + { 2875 + "a": "IH", 2876 + "b": "M", 2877 + "id": "IH_M", 2878 + "weight": 0.85 2879 + }, 2880 + { 2881 + "a": "IH", 2882 + "b": "N", 2883 + "id": "IH_N", 2884 + "weight": 0.85 2885 + }, 2886 + { 2887 + "a": "IH", 2888 + "b": "NG", 2889 + "id": "IH_NG", 2890 + "weight": 0.85 2891 + }, 2892 + { 2893 + "a": "IY", 2894 + "b": "M", 2895 + "id": "IY_M", 2896 + "weight": 0.85 2897 + }, 2898 + { 2899 + "a": "IY", 2900 + "b": "N", 2901 + "id": "IY_N", 2902 + "weight": 0.85 2903 + }, 2904 + { 2905 + "a": "IY", 2906 + "b": "NG", 2907 + "id": "IY_NG", 2908 + "weight": 0.85 2909 + }, 2910 + { 2911 + "a": "M", 2912 + "b": "AA", 2913 + "id": "M_AA", 2914 + "weight": 0.85 2915 + }, 2916 + { 2917 + "a": "M", 2918 + "b": "AE", 2919 + "id": "M_AE", 2920 + "weight": 0.85 2921 + }, 2922 + { 2923 + "a": "M", 2924 + "b": "AH", 2925 + "id": "M_AH", 2926 + "weight": 0.85 2927 + }, 2928 + { 2929 + "a": "M", 2930 + "b": "AO", 2931 + "id": "M_AO", 2932 + "weight": 0.85 2933 + }, 2934 + { 2935 + "a": "M", 2936 + "b": "AW", 2937 + "id": "M_AW", 2938 + "weight": 0.85 2939 + }, 2940 + { 2941 + "a": "M", 2942 + "b": "AY", 2943 + "id": "M_AY", 2944 + "weight": 0.85 2945 + }, 2946 + { 2947 + "a": "M", 2948 + "b": "EH", 2949 + "id": "M_EH", 2950 + "weight": 0.85 2951 + }, 2952 + { 2953 + "a": "M", 2954 + "b": "ER", 2955 + "id": "M_ER", 2956 + "weight": 0.85 2957 + }, 2958 + { 2959 + "a": "M", 2960 + "b": "EY", 2961 + "id": "M_EY", 2962 + "weight": 0.85 2963 + }, 2964 + { 2965 + "a": "M", 2966 + "b": "IH", 2967 + "id": "M_IH", 2968 + "weight": 0.85 2969 + }, 2970 + { 2971 + "a": "M", 2972 + "b": "IY", 2973 + "id": "M_IY", 2974 + "weight": 0.85 2975 + }, 2976 + { 2977 + "a": "M", 2978 + "b": "OW", 2979 + "id": "M_OW", 2980 + "weight": 0.85 2981 + }, 2982 + { 2983 + "a": "M", 2984 + "b": "OY", 2985 + "id": "M_OY", 2986 + "weight": 0.85 2987 + }, 2988 + { 2989 + "a": "M", 2990 + "b": "UH", 2991 + "id": "M_UH", 2992 + "weight": 0.85 2993 + }, 2994 + { 2995 + "a": "M", 2996 + "b": "UW", 2997 + "id": "M_UW", 2998 + "weight": 0.85 2999 + }, 3000 + { 3001 + "a": "N", 3002 + "b": "AA", 3003 + "id": "N_AA", 3004 + "weight": 0.85 3005 + }, 3006 + { 3007 + "a": "N", 3008 + "b": "AE", 3009 + "id": "N_AE", 3010 + "weight": 0.85 3011 + }, 3012 + { 3013 + "a": "N", 3014 + "b": "AH", 3015 + "id": "N_AH", 3016 + "weight": 0.85 3017 + }, 3018 + { 3019 + "a": "N", 3020 + "b": "AO", 3021 + "id": "N_AO", 3022 + "weight": 0.85 3023 + }, 3024 + { 3025 + "a": "N", 3026 + "b": "AW", 3027 + "id": "N_AW", 3028 + "weight": 0.85 3029 + }, 3030 + { 3031 + "a": "N", 3032 + "b": "AY", 3033 + "id": "N_AY", 3034 + "weight": 0.85 3035 + }, 3036 + { 3037 + "a": "N", 3038 + "b": "EH", 3039 + "id": "N_EH", 3040 + "weight": 0.85 3041 + }, 3042 + { 3043 + "a": "N", 3044 + "b": "ER", 3045 + "id": "N_ER", 3046 + "weight": 0.85 3047 + }, 3048 + { 3049 + "a": "N", 3050 + "b": "EY", 3051 + "id": "N_EY", 3052 + "weight": 0.85 3053 + }, 3054 + { 3055 + "a": "N", 3056 + "b": "IH", 3057 + "id": "N_IH", 3058 + "weight": 0.85 3059 + }, 3060 + { 3061 + "a": "N", 3062 + "b": "IY", 3063 + "id": "N_IY", 3064 + "weight": 0.85 3065 + }, 3066 + { 3067 + "a": "N", 3068 + "b": "OW", 3069 + "id": "N_OW", 3070 + "weight": 0.85 3071 + }, 3072 + { 3073 + "a": "N", 3074 + "b": "OY", 3075 + "id": "N_OY", 3076 + "weight": 0.85 3077 + }, 3078 + { 3079 + "a": "N", 3080 + "b": "UH", 3081 + "id": "N_UH", 3082 + "weight": 0.85 3083 + }, 3084 + { 3085 + "a": "N", 3086 + "b": "UW", 3087 + "id": "N_UW", 3088 + "weight": 0.85 3089 + }, 3090 + { 3091 + "a": "NG", 3092 + "b": "AA", 3093 + "id": "NG_AA", 3094 + "weight": 0.85 3095 + }, 3096 + { 3097 + "a": "NG", 3098 + "b": "AE", 3099 + "id": "NG_AE", 3100 + "weight": 0.85 3101 + }, 3102 + { 3103 + "a": "NG", 3104 + "b": "AH", 3105 + "id": "NG_AH", 3106 + "weight": 0.85 3107 + }, 3108 + { 3109 + "a": "NG", 3110 + "b": "AO", 3111 + "id": "NG_AO", 3112 + "weight": 0.85 3113 + }, 3114 + { 3115 + "a": "NG", 3116 + "b": "AW", 3117 + "id": "NG_AW", 3118 + "weight": 0.85 3119 + }, 3120 + { 3121 + "a": "NG", 3122 + "b": "AY", 3123 + "id": "NG_AY", 3124 + "weight": 0.85 3125 + }, 3126 + { 3127 + "a": "NG", 3128 + "b": "EH", 3129 + "id": "NG_EH", 3130 + "weight": 0.85 3131 + }, 3132 + { 3133 + "a": "NG", 3134 + "b": "ER", 3135 + "id": "NG_ER", 3136 + "weight": 0.85 3137 + }, 3138 + { 3139 + "a": "NG", 3140 + "b": "EY", 3141 + "id": "NG_EY", 3142 + "weight": 0.85 3143 + }, 3144 + { 3145 + "a": "NG", 3146 + "b": "IH", 3147 + "id": "NG_IH", 3148 + "weight": 0.85 3149 + }, 3150 + { 3151 + "a": "NG", 3152 + "b": "IY", 3153 + "id": "NG_IY", 3154 + "weight": 0.85 3155 + }, 3156 + { 3157 + "a": "NG", 3158 + "b": "OW", 3159 + "id": "NG_OW", 3160 + "weight": 0.85 3161 + }, 3162 + { 3163 + "a": "NG", 3164 + "b": "OY", 3165 + "id": "NG_OY", 3166 + "weight": 0.85 3167 + }, 3168 + { 3169 + "a": "NG", 3170 + "b": "UH", 3171 + "id": "NG_UH", 3172 + "weight": 0.85 3173 + }, 3174 + { 3175 + "a": "NG", 3176 + "b": "UW", 3177 + "id": "NG_UW", 3178 + "weight": 0.85 3179 + }, 3180 + { 3181 + "a": "OW", 3182 + "b": "M", 3183 + "id": "OW_M", 3184 + "weight": 0.85 3185 + }, 3186 + { 3187 + "a": "OW", 3188 + "b": "N", 3189 + "id": "OW_N", 3190 + "weight": 0.85 3191 + }, 3192 + { 3193 + "a": "OW", 3194 + "b": "NG", 3195 + "id": "OW_NG", 3196 + "weight": 0.85 3197 + }, 3198 + { 3199 + "a": "OY", 3200 + "b": "M", 3201 + "id": "OY_M", 3202 + "weight": 0.85 3203 + }, 3204 + { 3205 + "a": "OY", 3206 + "b": "N", 3207 + "id": "OY_N", 3208 + "weight": 0.85 3209 + }, 3210 + { 3211 + "a": "OY", 3212 + "b": "NG", 3213 + "id": "OY_NG", 3214 + "weight": 0.85 3215 + }, 3216 + { 3217 + "a": "UH", 3218 + "b": "M", 3219 + "id": "UH_M", 3220 + "weight": 0.85 3221 + }, 3222 + { 3223 + "a": "UH", 3224 + "b": "N", 3225 + "id": "UH_N", 3226 + "weight": 0.85 3227 + }, 3228 + { 3229 + "a": "UH", 3230 + "b": "NG", 3231 + "id": "UH_NG", 3232 + "weight": 0.85 3233 + }, 3234 + { 3235 + "a": "UW", 3236 + "b": "M", 3237 + "id": "UW_M", 3238 + "weight": 0.85 3239 + }, 3240 + { 3241 + "a": "UW", 3242 + "b": "N", 3243 + "id": "UW_N", 3244 + "weight": 0.85 3245 + }, 3246 + { 3247 + "a": "UW", 3248 + "b": "NG", 3249 + "id": "UW_NG", 3250 + "weight": 0.85 3251 + }, 3252 + { 3253 + "a": "AA", 3254 + "b": "DH", 3255 + "id": "AA_DH", 3256 + "weight": 0.8 3257 + }, 3258 + { 3259 + "a": "AA", 3260 + "b": "F", 3261 + "id": "AA_F", 3262 + "weight": 0.8 3263 + }, 3264 + { 3265 + "a": "AA", 3266 + "b": "HH", 3267 + "id": "AA_HH", 3268 + "weight": 0.8 3269 + }, 3270 + { 3271 + "a": "AA", 3272 + "b": "S", 3273 + "id": "AA_S", 3274 + "weight": 0.8 3275 + }, 3276 + { 3277 + "a": "AA", 3278 + "b": "SH", 3279 + "id": "AA_SH", 3280 + "weight": 0.8 3281 + }, 3282 + { 3283 + "a": "AA", 3284 + "b": "TH", 3285 + "id": "AA_TH", 3286 + "weight": 0.8 3287 + }, 3288 + { 3289 + "a": "AA", 3290 + "b": "V", 3291 + "id": "AA_V", 3292 + "weight": 0.8 3293 + }, 3294 + { 3295 + "a": "AA", 3296 + "b": "Z", 3297 + "id": "AA_Z", 3298 + "weight": 0.8 3299 + }, 3300 + { 3301 + "a": "AA", 3302 + "b": "ZH", 3303 + "id": "AA_ZH", 3304 + "weight": 0.8 3305 + }, 3306 + { 3307 + "a": "AE", 3308 + "b": "DH", 3309 + "id": "AE_DH", 3310 + "weight": 0.8 3311 + }, 3312 + { 3313 + "a": "AE", 3314 + "b": "F", 3315 + "id": "AE_F", 3316 + "weight": 0.8 3317 + }, 3318 + { 3319 + "a": "AE", 3320 + "b": "HH", 3321 + "id": "AE_HH", 3322 + "weight": 0.8 3323 + }, 3324 + { 3325 + "a": "AE", 3326 + "b": "S", 3327 + "id": "AE_S", 3328 + "weight": 0.8 3329 + }, 3330 + { 3331 + "a": "AE", 3332 + "b": "SH", 3333 + "id": "AE_SH", 3334 + "weight": 0.8 3335 + }, 3336 + { 3337 + "a": "AE", 3338 + "b": "TH", 3339 + "id": "AE_TH", 3340 + "weight": 0.8 3341 + }, 3342 + { 3343 + "a": "AE", 3344 + "b": "V", 3345 + "id": "AE_V", 3346 + "weight": 0.8 3347 + }, 3348 + { 3349 + "a": "AE", 3350 + "b": "Z", 3351 + "id": "AE_Z", 3352 + "weight": 0.8 3353 + }, 3354 + { 3355 + "a": "AE", 3356 + "b": "ZH", 3357 + "id": "AE_ZH", 3358 + "weight": 0.8 3359 + }, 3360 + { 3361 + "a": "AH", 3362 + "b": "DH", 3363 + "id": "AH_DH", 3364 + "weight": 0.8 3365 + }, 3366 + { 3367 + "a": "AH", 3368 + "b": "F", 3369 + "id": "AH_F", 3370 + "weight": 0.8 3371 + }, 3372 + { 3373 + "a": "AH", 3374 + "b": "HH", 3375 + "id": "AH_HH", 3376 + "weight": 0.8 3377 + }, 3378 + { 3379 + "a": "AH", 3380 + "b": "S", 3381 + "id": "AH_S", 3382 + "weight": 0.8 3383 + }, 3384 + { 3385 + "a": "AH", 3386 + "b": "SH", 3387 + "id": "AH_SH", 3388 + "weight": 0.8 3389 + }, 3390 + { 3391 + "a": "AH", 3392 + "b": "TH", 3393 + "id": "AH_TH", 3394 + "weight": 0.8 3395 + }, 3396 + { 3397 + "a": "AH", 3398 + "b": "V", 3399 + "id": "AH_V", 3400 + "weight": 0.8 3401 + }, 3402 + { 3403 + "a": "AH", 3404 + "b": "Z", 3405 + "id": "AH_Z", 3406 + "weight": 0.8 3407 + }, 3408 + { 3409 + "a": "AH", 3410 + "b": "ZH", 3411 + "id": "AH_ZH", 3412 + "weight": 0.8 3413 + }, 3414 + { 3415 + "a": "AO", 3416 + "b": "DH", 3417 + "id": "AO_DH", 3418 + "weight": 0.8 3419 + }, 3420 + { 3421 + "a": "AO", 3422 + "b": "F", 3423 + "id": "AO_F", 3424 + "weight": 0.8 3425 + }, 3426 + { 3427 + "a": "AO", 3428 + "b": "HH", 3429 + "id": "AO_HH", 3430 + "weight": 0.8 3431 + }, 3432 + { 3433 + "a": "AO", 3434 + "b": "S", 3435 + "id": "AO_S", 3436 + "weight": 0.8 3437 + }, 3438 + { 3439 + "a": "AO", 3440 + "b": "SH", 3441 + "id": "AO_SH", 3442 + "weight": 0.8 3443 + }, 3444 + { 3445 + "a": "AO", 3446 + "b": "TH", 3447 + "id": "AO_TH", 3448 + "weight": 0.8 3449 + }, 3450 + { 3451 + "a": "AO", 3452 + "b": "V", 3453 + "id": "AO_V", 3454 + "weight": 0.8 3455 + }, 3456 + { 3457 + "a": "AO", 3458 + "b": "Z", 3459 + "id": "AO_Z", 3460 + "weight": 0.8 3461 + }, 3462 + { 3463 + "a": "AO", 3464 + "b": "ZH", 3465 + "id": "AO_ZH", 3466 + "weight": 0.8 3467 + }, 3468 + { 3469 + "a": "AW", 3470 + "b": "DH", 3471 + "id": "AW_DH", 3472 + "weight": 0.8 3473 + }, 3474 + { 3475 + "a": "AW", 3476 + "b": "F", 3477 + "id": "AW_F", 3478 + "weight": 0.8 3479 + }, 3480 + { 3481 + "a": "AW", 3482 + "b": "HH", 3483 + "id": "AW_HH", 3484 + "weight": 0.8 3485 + }, 3486 + { 3487 + "a": "AW", 3488 + "b": "S", 3489 + "id": "AW_S", 3490 + "weight": 0.8 3491 + }, 3492 + { 3493 + "a": "AW", 3494 + "b": "SH", 3495 + "id": "AW_SH", 3496 + "weight": 0.8 3497 + }, 3498 + { 3499 + "a": "AW", 3500 + "b": "TH", 3501 + "id": "AW_TH", 3502 + "weight": 0.8 3503 + }, 3504 + { 3505 + "a": "AW", 3506 + "b": "V", 3507 + "id": "AW_V", 3508 + "weight": 0.8 3509 + }, 3510 + { 3511 + "a": "AW", 3512 + "b": "Z", 3513 + "id": "AW_Z", 3514 + "weight": 0.8 3515 + }, 3516 + { 3517 + "a": "AW", 3518 + "b": "ZH", 3519 + "id": "AW_ZH", 3520 + "weight": 0.8 3521 + }, 3522 + { 3523 + "a": "AY", 3524 + "b": "DH", 3525 + "id": "AY_DH", 3526 + "weight": 0.8 3527 + }, 3528 + { 3529 + "a": "AY", 3530 + "b": "F", 3531 + "id": "AY_F", 3532 + "weight": 0.8 3533 + }, 3534 + { 3535 + "a": "AY", 3536 + "b": "HH", 3537 + "id": "AY_HH", 3538 + "weight": 0.8 3539 + }, 3540 + { 3541 + "a": "AY", 3542 + "b": "S", 3543 + "id": "AY_S", 3544 + "weight": 0.8 3545 + }, 3546 + { 3547 + "a": "AY", 3548 + "b": "SH", 3549 + "id": "AY_SH", 3550 + "weight": 0.8 3551 + }, 3552 + { 3553 + "a": "AY", 3554 + "b": "TH", 3555 + "id": "AY_TH", 3556 + "weight": 0.8 3557 + }, 3558 + { 3559 + "a": "AY", 3560 + "b": "V", 3561 + "id": "AY_V", 3562 + "weight": 0.8 3563 + }, 3564 + { 3565 + "a": "AY", 3566 + "b": "Z", 3567 + "id": "AY_Z", 3568 + "weight": 0.8 3569 + }, 3570 + { 3571 + "a": "AY", 3572 + "b": "ZH", 3573 + "id": "AY_ZH", 3574 + "weight": 0.8 3575 + }, 3576 + { 3577 + "a": "DH", 3578 + "b": "AA", 3579 + "id": "DH_AA", 3580 + "weight": 0.8 3581 + }, 3582 + { 3583 + "a": "DH", 3584 + "b": "AE", 3585 + "id": "DH_AE", 3586 + "weight": 0.8 3587 + }, 3588 + { 3589 + "a": "DH", 3590 + "b": "AH", 3591 + "id": "DH_AH", 3592 + "weight": 0.8 3593 + }, 3594 + { 3595 + "a": "DH", 3596 + "b": "AO", 3597 + "id": "DH_AO", 3598 + "weight": 0.8 3599 + }, 3600 + { 3601 + "a": "DH", 3602 + "b": "AW", 3603 + "id": "DH_AW", 3604 + "weight": 0.8 3605 + }, 3606 + { 3607 + "a": "DH", 3608 + "b": "AY", 3609 + "id": "DH_AY", 3610 + "weight": 0.8 3611 + }, 3612 + { 3613 + "a": "DH", 3614 + "b": "EH", 3615 + "id": "DH_EH", 3616 + "weight": 0.8 3617 + }, 3618 + { 3619 + "a": "DH", 3620 + "b": "ER", 3621 + "id": "DH_ER", 3622 + "weight": 0.8 3623 + }, 3624 + { 3625 + "a": "DH", 3626 + "b": "EY", 3627 + "id": "DH_EY", 3628 + "weight": 0.8 3629 + }, 3630 + { 3631 + "a": "DH", 3632 + "b": "IH", 3633 + "id": "DH_IH", 3634 + "weight": 0.8 3635 + }, 3636 + { 3637 + "a": "DH", 3638 + "b": "IY", 3639 + "id": "DH_IY", 3640 + "weight": 0.8 3641 + }, 3642 + { 3643 + "a": "DH", 3644 + "b": "OW", 3645 + "id": "DH_OW", 3646 + "weight": 0.8 3647 + }, 3648 + { 3649 + "a": "DH", 3650 + "b": "OY", 3651 + "id": "DH_OY", 3652 + "weight": 0.8 3653 + }, 3654 + { 3655 + "a": "DH", 3656 + "b": "UH", 3657 + "id": "DH_UH", 3658 + "weight": 0.8 3659 + }, 3660 + { 3661 + "a": "DH", 3662 + "b": "UW", 3663 + "id": "DH_UW", 3664 + "weight": 0.8 3665 + }, 3666 + { 3667 + "a": "EH", 3668 + "b": "DH", 3669 + "id": "EH_DH", 3670 + "weight": 0.8 3671 + }, 3672 + { 3673 + "a": "EH", 3674 + "b": "F", 3675 + "id": "EH_F", 3676 + "weight": 0.8 3677 + }, 3678 + { 3679 + "a": "EH", 3680 + "b": "HH", 3681 + "id": "EH_HH", 3682 + "weight": 0.8 3683 + }, 3684 + { 3685 + "a": "EH", 3686 + "b": "S", 3687 + "id": "EH_S", 3688 + "weight": 0.8 3689 + }, 3690 + { 3691 + "a": "EH", 3692 + "b": "SH", 3693 + "id": "EH_SH", 3694 + "weight": 0.8 3695 + }, 3696 + { 3697 + "a": "EH", 3698 + "b": "TH", 3699 + "id": "EH_TH", 3700 + "weight": 0.8 3701 + }, 3702 + { 3703 + "a": "EH", 3704 + "b": "V", 3705 + "id": "EH_V", 3706 + "weight": 0.8 3707 + }, 3708 + { 3709 + "a": "EH", 3710 + "b": "Z", 3711 + "id": "EH_Z", 3712 + "weight": 0.8 3713 + }, 3714 + { 3715 + "a": "EH", 3716 + "b": "ZH", 3717 + "id": "EH_ZH", 3718 + "weight": 0.8 3719 + }, 3720 + { 3721 + "a": "ER", 3722 + "b": "DH", 3723 + "id": "ER_DH", 3724 + "weight": 0.8 3725 + }, 3726 + { 3727 + "a": "ER", 3728 + "b": "F", 3729 + "id": "ER_F", 3730 + "weight": 0.8 3731 + }, 3732 + { 3733 + "a": "ER", 3734 + "b": "HH", 3735 + "id": "ER_HH", 3736 + "weight": 0.8 3737 + }, 3738 + { 3739 + "a": "ER", 3740 + "b": "S", 3741 + "id": "ER_S", 3742 + "weight": 0.8 3743 + }, 3744 + { 3745 + "a": "ER", 3746 + "b": "SH", 3747 + "id": "ER_SH", 3748 + "weight": 0.8 3749 + }, 3750 + { 3751 + "a": "ER", 3752 + "b": "TH", 3753 + "id": "ER_TH", 3754 + "weight": 0.8 3755 + }, 3756 + { 3757 + "a": "ER", 3758 + "b": "V", 3759 + "id": "ER_V", 3760 + "weight": 0.8 3761 + }, 3762 + { 3763 + "a": "ER", 3764 + "b": "Z", 3765 + "id": "ER_Z", 3766 + "weight": 0.8 3767 + }, 3768 + { 3769 + "a": "ER", 3770 + "b": "ZH", 3771 + "id": "ER_ZH", 3772 + "weight": 0.8 3773 + }, 3774 + { 3775 + "a": "EY", 3776 + "b": "DH", 3777 + "id": "EY_DH", 3778 + "weight": 0.8 3779 + }, 3780 + { 3781 + "a": "EY", 3782 + "b": "F", 3783 + "id": "EY_F", 3784 + "weight": 0.8 3785 + }, 3786 + { 3787 + "a": "EY", 3788 + "b": "HH", 3789 + "id": "EY_HH", 3790 + "weight": 0.8 3791 + }, 3792 + { 3793 + "a": "EY", 3794 + "b": "S", 3795 + "id": "EY_S", 3796 + "weight": 0.8 3797 + }, 3798 + { 3799 + "a": "EY", 3800 + "b": "SH", 3801 + "id": "EY_SH", 3802 + "weight": 0.8 3803 + }, 3804 + { 3805 + "a": "EY", 3806 + "b": "TH", 3807 + "id": "EY_TH", 3808 + "weight": 0.8 3809 + }, 3810 + { 3811 + "a": "EY", 3812 + "b": "V", 3813 + "id": "EY_V", 3814 + "weight": 0.8 3815 + }, 3816 + { 3817 + "a": "EY", 3818 + "b": "Z", 3819 + "id": "EY_Z", 3820 + "weight": 0.8 3821 + }, 3822 + { 3823 + "a": "EY", 3824 + "b": "ZH", 3825 + "id": "EY_ZH", 3826 + "weight": 0.8 3827 + }, 3828 + { 3829 + "a": "F", 3830 + "b": "AA", 3831 + "id": "F_AA", 3832 + "weight": 0.8 3833 + }, 3834 + { 3835 + "a": "F", 3836 + "b": "AE", 3837 + "id": "F_AE", 3838 + "weight": 0.8 3839 + }, 3840 + { 3841 + "a": "F", 3842 + "b": "AH", 3843 + "id": "F_AH", 3844 + "weight": 0.8 3845 + }, 3846 + { 3847 + "a": "F", 3848 + "b": "AO", 3849 + "id": "F_AO", 3850 + "weight": 0.8 3851 + }, 3852 + { 3853 + "a": "F", 3854 + "b": "AW", 3855 + "id": "F_AW", 3856 + "weight": 0.8 3857 + }, 3858 + { 3859 + "a": "F", 3860 + "b": "AY", 3861 + "id": "F_AY", 3862 + "weight": 0.8 3863 + }, 3864 + { 3865 + "a": "F", 3866 + "b": "EH", 3867 + "id": "F_EH", 3868 + "weight": 0.8 3869 + }, 3870 + { 3871 + "a": "F", 3872 + "b": "ER", 3873 + "id": "F_ER", 3874 + "weight": 0.8 3875 + }, 3876 + { 3877 + "a": "F", 3878 + "b": "EY", 3879 + "id": "F_EY", 3880 + "weight": 0.8 3881 + }, 3882 + { 3883 + "a": "F", 3884 + "b": "IH", 3885 + "id": "F_IH", 3886 + "weight": 0.8 3887 + }, 3888 + { 3889 + "a": "F", 3890 + "b": "IY", 3891 + "id": "F_IY", 3892 + "weight": 0.8 3893 + }, 3894 + { 3895 + "a": "F", 3896 + "b": "OW", 3897 + "id": "F_OW", 3898 + "weight": 0.8 3899 + }, 3900 + { 3901 + "a": "F", 3902 + "b": "OY", 3903 + "id": "F_OY", 3904 + "weight": 0.8 3905 + }, 3906 + { 3907 + "a": "F", 3908 + "b": "UH", 3909 + "id": "F_UH", 3910 + "weight": 0.8 3911 + }, 3912 + { 3913 + "a": "F", 3914 + "b": "UW", 3915 + "id": "F_UW", 3916 + "weight": 0.8 3917 + }, 3918 + { 3919 + "a": "HH", 3920 + "b": "AA", 3921 + "id": "HH_AA", 3922 + "weight": 0.8 3923 + }, 3924 + { 3925 + "a": "HH", 3926 + "b": "AE", 3927 + "id": "HH_AE", 3928 + "weight": 0.8 3929 + }, 3930 + { 3931 + "a": "HH", 3932 + "b": "AH", 3933 + "id": "HH_AH", 3934 + "weight": 0.8 3935 + }, 3936 + { 3937 + "a": "HH", 3938 + "b": "AO", 3939 + "id": "HH_AO", 3940 + "weight": 0.8 3941 + }, 3942 + { 3943 + "a": "HH", 3944 + "b": "AW", 3945 + "id": "HH_AW", 3946 + "weight": 0.8 3947 + }, 3948 + { 3949 + "a": "HH", 3950 + "b": "AY", 3951 + "id": "HH_AY", 3952 + "weight": 0.8 3953 + }, 3954 + { 3955 + "a": "HH", 3956 + "b": "EH", 3957 + "id": "HH_EH", 3958 + "weight": 0.8 3959 + }, 3960 + { 3961 + "a": "HH", 3962 + "b": "ER", 3963 + "id": "HH_ER", 3964 + "weight": 0.8 3965 + }, 3966 + { 3967 + "a": "HH", 3968 + "b": "EY", 3969 + "id": "HH_EY", 3970 + "weight": 0.8 3971 + }, 3972 + { 3973 + "a": "HH", 3974 + "b": "IH", 3975 + "id": "HH_IH", 3976 + "weight": 0.8 3977 + }, 3978 + { 3979 + "a": "HH", 3980 + "b": "IY", 3981 + "id": "HH_IY", 3982 + "weight": 0.8 3983 + }, 3984 + { 3985 + "a": "HH", 3986 + "b": "OW", 3987 + "id": "HH_OW", 3988 + "weight": 0.8 3989 + }, 3990 + { 3991 + "a": "HH", 3992 + "b": "OY", 3993 + "id": "HH_OY", 3994 + "weight": 0.8 3995 + }, 3996 + { 3997 + "a": "HH", 3998 + "b": "UH", 3999 + "id": "HH_UH", 4000 + "weight": 0.8 4001 + }, 4002 + { 4003 + "a": "HH", 4004 + "b": "UW", 4005 + "id": "HH_UW", 4006 + "weight": 0.8 4007 + }, 4008 + { 4009 + "a": "IH", 4010 + "b": "DH", 4011 + "id": "IH_DH", 4012 + "weight": 0.8 4013 + }, 4014 + { 4015 + "a": "IH", 4016 + "b": "F", 4017 + "id": "IH_F", 4018 + "weight": 0.8 4019 + }, 4020 + { 4021 + "a": "IH", 4022 + "b": "HH", 4023 + "id": "IH_HH", 4024 + "weight": 0.8 4025 + }, 4026 + { 4027 + "a": "IH", 4028 + "b": "S", 4029 + "id": "IH_S", 4030 + "weight": 0.8 4031 + }, 4032 + { 4033 + "a": "IH", 4034 + "b": "SH", 4035 + "id": "IH_SH", 4036 + "weight": 0.8 4037 + }, 4038 + { 4039 + "a": "IH", 4040 + "b": "TH", 4041 + "id": "IH_TH", 4042 + "weight": 0.8 4043 + }, 4044 + { 4045 + "a": "IH", 4046 + "b": "V", 4047 + "id": "IH_V", 4048 + "weight": 0.8 4049 + }, 4050 + { 4051 + "a": "IH", 4052 + "b": "Z", 4053 + "id": "IH_Z", 4054 + "weight": 0.8 4055 + }, 4056 + { 4057 + "a": "IH", 4058 + "b": "ZH", 4059 + "id": "IH_ZH", 4060 + "weight": 0.8 4061 + }, 4062 + { 4063 + "a": "IY", 4064 + "b": "DH", 4065 + "id": "IY_DH", 4066 + "weight": 0.8 4067 + }, 4068 + { 4069 + "a": "IY", 4070 + "b": "F", 4071 + "id": "IY_F", 4072 + "weight": 0.8 4073 + }, 4074 + { 4075 + "a": "IY", 4076 + "b": "HH", 4077 + "id": "IY_HH", 4078 + "weight": 0.8 4079 + }, 4080 + { 4081 + "a": "IY", 4082 + "b": "S", 4083 + "id": "IY_S", 4084 + "weight": 0.8 4085 + }, 4086 + { 4087 + "a": "IY", 4088 + "b": "SH", 4089 + "id": "IY_SH", 4090 + "weight": 0.8 4091 + }, 4092 + { 4093 + "a": "IY", 4094 + "b": "TH", 4095 + "id": "IY_TH", 4096 + "weight": 0.8 4097 + }, 4098 + { 4099 + "a": "IY", 4100 + "b": "V", 4101 + "id": "IY_V", 4102 + "weight": 0.8 4103 + }, 4104 + { 4105 + "a": "IY", 4106 + "b": "Z", 4107 + "id": "IY_Z", 4108 + "weight": 0.8 4109 + }, 4110 + { 4111 + "a": "IY", 4112 + "b": "ZH", 4113 + "id": "IY_ZH", 4114 + "weight": 0.8 4115 + }, 4116 + { 4117 + "a": "OW", 4118 + "b": "DH", 4119 + "id": "OW_DH", 4120 + "weight": 0.8 4121 + }, 4122 + { 4123 + "a": "OW", 4124 + "b": "F", 4125 + "id": "OW_F", 4126 + "weight": 0.8 4127 + }, 4128 + { 4129 + "a": "OW", 4130 + "b": "HH", 4131 + "id": "OW_HH", 4132 + "weight": 0.8 4133 + }, 4134 + { 4135 + "a": "OW", 4136 + "b": "S", 4137 + "id": "OW_S", 4138 + "weight": 0.8 4139 + }, 4140 + { 4141 + "a": "OW", 4142 + "b": "SH", 4143 + "id": "OW_SH", 4144 + "weight": 0.8 4145 + }, 4146 + { 4147 + "a": "OW", 4148 + "b": "TH", 4149 + "id": "OW_TH", 4150 + "weight": 0.8 4151 + }, 4152 + { 4153 + "a": "OW", 4154 + "b": "V", 4155 + "id": "OW_V", 4156 + "weight": 0.8 4157 + }, 4158 + { 4159 + "a": "OW", 4160 + "b": "Z", 4161 + "id": "OW_Z", 4162 + "weight": 0.8 4163 + }, 4164 + { 4165 + "a": "OW", 4166 + "b": "ZH", 4167 + "id": "OW_ZH", 4168 + "weight": 0.8 4169 + }, 4170 + { 4171 + "a": "OY", 4172 + "b": "DH", 4173 + "id": "OY_DH", 4174 + "weight": 0.8 4175 + }, 4176 + { 4177 + "a": "OY", 4178 + "b": "F", 4179 + "id": "OY_F", 4180 + "weight": 0.8 4181 + }, 4182 + { 4183 + "a": "OY", 4184 + "b": "HH", 4185 + "id": "OY_HH", 4186 + "weight": 0.8 4187 + }, 4188 + { 4189 + "a": "OY", 4190 + "b": "S", 4191 + "id": "OY_S", 4192 + "weight": 0.8 4193 + }, 4194 + { 4195 + "a": "OY", 4196 + "b": "SH", 4197 + "id": "OY_SH", 4198 + "weight": 0.8 4199 + }, 4200 + { 4201 + "a": "OY", 4202 + "b": "TH", 4203 + "id": "OY_TH", 4204 + "weight": 0.8 4205 + }, 4206 + { 4207 + "a": "OY", 4208 + "b": "V", 4209 + "id": "OY_V", 4210 + "weight": 0.8 4211 + }, 4212 + { 4213 + "a": "OY", 4214 + "b": "Z", 4215 + "id": "OY_Z", 4216 + "weight": 0.8 4217 + }, 4218 + { 4219 + "a": "OY", 4220 + "b": "ZH", 4221 + "id": "OY_ZH", 4222 + "weight": 0.8 4223 + }, 4224 + { 4225 + "a": "S", 4226 + "b": "AA", 4227 + "id": "S_AA", 4228 + "weight": 0.8 4229 + }, 4230 + { 4231 + "a": "S", 4232 + "b": "AE", 4233 + "id": "S_AE", 4234 + "weight": 0.8 4235 + }, 4236 + { 4237 + "a": "S", 4238 + "b": "AH", 4239 + "id": "S_AH", 4240 + "weight": 0.8 4241 + }, 4242 + { 4243 + "a": "S", 4244 + "b": "AO", 4245 + "id": "S_AO", 4246 + "weight": 0.8 4247 + }, 4248 + { 4249 + "a": "S", 4250 + "b": "AW", 4251 + "id": "S_AW", 4252 + "weight": 0.8 4253 + }, 4254 + { 4255 + "a": "S", 4256 + "b": "AY", 4257 + "id": "S_AY", 4258 + "weight": 0.8 4259 + }, 4260 + { 4261 + "a": "S", 4262 + "b": "EH", 4263 + "id": "S_EH", 4264 + "weight": 0.8 4265 + }, 4266 + { 4267 + "a": "S", 4268 + "b": "ER", 4269 + "id": "S_ER", 4270 + "weight": 0.8 4271 + }, 4272 + { 4273 + "a": "S", 4274 + "b": "EY", 4275 + "id": "S_EY", 4276 + "weight": 0.8 4277 + }, 4278 + { 4279 + "a": "S", 4280 + "b": "IH", 4281 + "id": "S_IH", 4282 + "weight": 0.8 4283 + }, 4284 + { 4285 + "a": "S", 4286 + "b": "IY", 4287 + "id": "S_IY", 4288 + "weight": 0.8 4289 + }, 4290 + { 4291 + "a": "S", 4292 + "b": "OW", 4293 + "id": "S_OW", 4294 + "weight": 0.8 4295 + }, 4296 + { 4297 + "a": "S", 4298 + "b": "OY", 4299 + "id": "S_OY", 4300 + "weight": 0.8 4301 + }, 4302 + { 4303 + "a": "S", 4304 + "b": "UH", 4305 + "id": "S_UH", 4306 + "weight": 0.8 4307 + }, 4308 + { 4309 + "a": "S", 4310 + "b": "UW", 4311 + "id": "S_UW", 4312 + "weight": 0.8 4313 + }, 4314 + { 4315 + "a": "SH", 4316 + "b": "AA", 4317 + "id": "SH_AA", 4318 + "weight": 0.8 4319 + }, 4320 + { 4321 + "a": "SH", 4322 + "b": "AE", 4323 + "id": "SH_AE", 4324 + "weight": 0.8 4325 + }, 4326 + { 4327 + "a": "SH", 4328 + "b": "AH", 4329 + "id": "SH_AH", 4330 + "weight": 0.8 4331 + }, 4332 + { 4333 + "a": "SH", 4334 + "b": "AO", 4335 + "id": "SH_AO", 4336 + "weight": 0.8 4337 + }, 4338 + { 4339 + "a": "SH", 4340 + "b": "AW", 4341 + "id": "SH_AW", 4342 + "weight": 0.8 4343 + }, 4344 + { 4345 + "a": "SH", 4346 + "b": "AY", 4347 + "id": "SH_AY", 4348 + "weight": 0.8 4349 + }, 4350 + { 4351 + "a": "SH", 4352 + "b": "EH", 4353 + "id": "SH_EH", 4354 + "weight": 0.8 4355 + }, 4356 + { 4357 + "a": "SH", 4358 + "b": "ER", 4359 + "id": "SH_ER", 4360 + "weight": 0.8 4361 + }, 4362 + { 4363 + "a": "SH", 4364 + "b": "EY", 4365 + "id": "SH_EY", 4366 + "weight": 0.8 4367 + }, 4368 + { 4369 + "a": "SH", 4370 + "b": "IH", 4371 + "id": "SH_IH", 4372 + "weight": 0.8 4373 + }, 4374 + { 4375 + "a": "SH", 4376 + "b": "IY", 4377 + "id": "SH_IY", 4378 + "weight": 0.8 4379 + }, 4380 + { 4381 + "a": "SH", 4382 + "b": "OW", 4383 + "id": "SH_OW", 4384 + "weight": 0.8 4385 + }, 4386 + { 4387 + "a": "SH", 4388 + "b": "OY", 4389 + "id": "SH_OY", 4390 + "weight": 0.8 4391 + }, 4392 + { 4393 + "a": "SH", 4394 + "b": "UH", 4395 + "id": "SH_UH", 4396 + "weight": 0.8 4397 + }, 4398 + { 4399 + "a": "SH", 4400 + "b": "UW", 4401 + "id": "SH_UW", 4402 + "weight": 0.8 4403 + }, 4404 + { 4405 + "a": "TH", 4406 + "b": "AA", 4407 + "id": "TH_AA", 4408 + "weight": 0.8 4409 + }, 4410 + { 4411 + "a": "TH", 4412 + "b": "AE", 4413 + "id": "TH_AE", 4414 + "weight": 0.8 4415 + }, 4416 + { 4417 + "a": "TH", 4418 + "b": "AH", 4419 + "id": "TH_AH", 4420 + "weight": 0.8 4421 + }, 4422 + { 4423 + "a": "TH", 4424 + "b": "AO", 4425 + "id": "TH_AO", 4426 + "weight": 0.8 4427 + }, 4428 + { 4429 + "a": "TH", 4430 + "b": "AW", 4431 + "id": "TH_AW", 4432 + "weight": 0.8 4433 + }, 4434 + { 4435 + "a": "TH", 4436 + "b": "AY", 4437 + "id": "TH_AY", 4438 + "weight": 0.8 4439 + }, 4440 + { 4441 + "a": "TH", 4442 + "b": "EH", 4443 + "id": "TH_EH", 4444 + "weight": 0.8 4445 + }, 4446 + { 4447 + "a": "TH", 4448 + "b": "ER", 4449 + "id": "TH_ER", 4450 + "weight": 0.8 4451 + }, 4452 + { 4453 + "a": "TH", 4454 + "b": "EY", 4455 + "id": "TH_EY", 4456 + "weight": 0.8 4457 + }, 4458 + { 4459 + "a": "TH", 4460 + "b": "IH", 4461 + "id": "TH_IH", 4462 + "weight": 0.8 4463 + }, 4464 + { 4465 + "a": "TH", 4466 + "b": "IY", 4467 + "id": "TH_IY", 4468 + "weight": 0.8 4469 + }, 4470 + { 4471 + "a": "TH", 4472 + "b": "OW", 4473 + "id": "TH_OW", 4474 + "weight": 0.8 4475 + }, 4476 + { 4477 + "a": "TH", 4478 + "b": "OY", 4479 + "id": "TH_OY", 4480 + "weight": 0.8 4481 + }, 4482 + { 4483 + "a": "TH", 4484 + "b": "UH", 4485 + "id": "TH_UH", 4486 + "weight": 0.8 4487 + }, 4488 + { 4489 + "a": "TH", 4490 + "b": "UW", 4491 + "id": "TH_UW", 4492 + "weight": 0.8 4493 + }, 4494 + { 4495 + "a": "UH", 4496 + "b": "DH", 4497 + "id": "UH_DH", 4498 + "weight": 0.8 4499 + }, 4500 + { 4501 + "a": "UH", 4502 + "b": "F", 4503 + "id": "UH_F", 4504 + "weight": 0.8 4505 + }, 4506 + { 4507 + "a": "UH", 4508 + "b": "HH", 4509 + "id": "UH_HH", 4510 + "weight": 0.8 4511 + }, 4512 + { 4513 + "a": "UH", 4514 + "b": "S", 4515 + "id": "UH_S", 4516 + "weight": 0.8 4517 + }, 4518 + { 4519 + "a": "UH", 4520 + "b": "SH", 4521 + "id": "UH_SH", 4522 + "weight": 0.8 4523 + }, 4524 + { 4525 + "a": "UH", 4526 + "b": "TH", 4527 + "id": "UH_TH", 4528 + "weight": 0.8 4529 + }, 4530 + { 4531 + "a": "UH", 4532 + "b": "V", 4533 + "id": "UH_V", 4534 + "weight": 0.8 4535 + }, 4536 + { 4537 + "a": "UH", 4538 + "b": "Z", 4539 + "id": "UH_Z", 4540 + "weight": 0.8 4541 + }, 4542 + { 4543 + "a": "UH", 4544 + "b": "ZH", 4545 + "id": "UH_ZH", 4546 + "weight": 0.8 4547 + }, 4548 + { 4549 + "a": "UW", 4550 + "b": "DH", 4551 + "id": "UW_DH", 4552 + "weight": 0.8 4553 + }, 4554 + { 4555 + "a": "UW", 4556 + "b": "F", 4557 + "id": "UW_F", 4558 + "weight": 0.8 4559 + }, 4560 + { 4561 + "a": "UW", 4562 + "b": "HH", 4563 + "id": "UW_HH", 4564 + "weight": 0.8 4565 + }, 4566 + { 4567 + "a": "UW", 4568 + "b": "S", 4569 + "id": "UW_S", 4570 + "weight": 0.8 4571 + }, 4572 + { 4573 + "a": "UW", 4574 + "b": "SH", 4575 + "id": "UW_SH", 4576 + "weight": 0.8 4577 + }, 4578 + { 4579 + "a": "UW", 4580 + "b": "TH", 4581 + "id": "UW_TH", 4582 + "weight": 0.8 4583 + }, 4584 + { 4585 + "a": "UW", 4586 + "b": "V", 4587 + "id": "UW_V", 4588 + "weight": 0.8 4589 + }, 4590 + { 4591 + "a": "UW", 4592 + "b": "Z", 4593 + "id": "UW_Z", 4594 + "weight": 0.8 4595 + }, 4596 + { 4597 + "a": "UW", 4598 + "b": "ZH", 4599 + "id": "UW_ZH", 4600 + "weight": 0.8 4601 + }, 4602 + { 4603 + "a": "V", 4604 + "b": "AA", 4605 + "id": "V_AA", 4606 + "weight": 0.8 4607 + }, 4608 + { 4609 + "a": "V", 4610 + "b": "AE", 4611 + "id": "V_AE", 4612 + "weight": 0.8 4613 + }, 4614 + { 4615 + "a": "V", 4616 + "b": "AH", 4617 + "id": "V_AH", 4618 + "weight": 0.8 4619 + }, 4620 + { 4621 + "a": "V", 4622 + "b": "AO", 4623 + "id": "V_AO", 4624 + "weight": 0.8 4625 + }, 4626 + { 4627 + "a": "V", 4628 + "b": "AW", 4629 + "id": "V_AW", 4630 + "weight": 0.8 4631 + }, 4632 + { 4633 + "a": "V", 4634 + "b": "AY", 4635 + "id": "V_AY", 4636 + "weight": 0.8 4637 + }, 4638 + { 4639 + "a": "V", 4640 + "b": "EH", 4641 + "id": "V_EH", 4642 + "weight": 0.8 4643 + }, 4644 + { 4645 + "a": "V", 4646 + "b": "ER", 4647 + "id": "V_ER", 4648 + "weight": 0.8 4649 + }, 4650 + { 4651 + "a": "V", 4652 + "b": "EY", 4653 + "id": "V_EY", 4654 + "weight": 0.8 4655 + }, 4656 + { 4657 + "a": "V", 4658 + "b": "IH", 4659 + "id": "V_IH", 4660 + "weight": 0.8 4661 + }, 4662 + { 4663 + "a": "V", 4664 + "b": "IY", 4665 + "id": "V_IY", 4666 + "weight": 0.8 4667 + }, 4668 + { 4669 + "a": "V", 4670 + "b": "OW", 4671 + "id": "V_OW", 4672 + "weight": 0.8 4673 + }, 4674 + { 4675 + "a": "V", 4676 + "b": "OY", 4677 + "id": "V_OY", 4678 + "weight": 0.8 4679 + }, 4680 + { 4681 + "a": "V", 4682 + "b": "UH", 4683 + "id": "V_UH", 4684 + "weight": 0.8 4685 + }, 4686 + { 4687 + "a": "V", 4688 + "b": "UW", 4689 + "id": "V_UW", 4690 + "weight": 0.8 4691 + }, 4692 + { 4693 + "a": "Z", 4694 + "b": "AA", 4695 + "id": "Z_AA", 4696 + "weight": 0.8 4697 + }, 4698 + { 4699 + "a": "Z", 4700 + "b": "AE", 4701 + "id": "Z_AE", 4702 + "weight": 0.8 4703 + }, 4704 + { 4705 + "a": "Z", 4706 + "b": "AH", 4707 + "id": "Z_AH", 4708 + "weight": 0.8 4709 + }, 4710 + { 4711 + "a": "Z", 4712 + "b": "AO", 4713 + "id": "Z_AO", 4714 + "weight": 0.8 4715 + }, 4716 + { 4717 + "a": "Z", 4718 + "b": "AW", 4719 + "id": "Z_AW", 4720 + "weight": 0.8 4721 + }, 4722 + { 4723 + "a": "Z", 4724 + "b": "AY", 4725 + "id": "Z_AY", 4726 + "weight": 0.8 4727 + }, 4728 + { 4729 + "a": "Z", 4730 + "b": "EH", 4731 + "id": "Z_EH", 4732 + "weight": 0.8 4733 + }, 4734 + { 4735 + "a": "Z", 4736 + "b": "ER", 4737 + "id": "Z_ER", 4738 + "weight": 0.8 4739 + }, 4740 + { 4741 + "a": "Z", 4742 + "b": "EY", 4743 + "id": "Z_EY", 4744 + "weight": 0.8 4745 + }, 4746 + { 4747 + "a": "Z", 4748 + "b": "IH", 4749 + "id": "Z_IH", 4750 + "weight": 0.8 4751 + }, 4752 + { 4753 + "a": "Z", 4754 + "b": "IY", 4755 + "id": "Z_IY", 4756 + "weight": 0.8 4757 + }, 4758 + { 4759 + "a": "Z", 4760 + "b": "OW", 4761 + "id": "Z_OW", 4762 + "weight": 0.8 4763 + }, 4764 + { 4765 + "a": "Z", 4766 + "b": "OY", 4767 + "id": "Z_OY", 4768 + "weight": 0.8 4769 + }, 4770 + { 4771 + "a": "Z", 4772 + "b": "UH", 4773 + "id": "Z_UH", 4774 + "weight": 0.8 4775 + }, 4776 + { 4777 + "a": "Z", 4778 + "b": "UW", 4779 + "id": "Z_UW", 4780 + "weight": 0.8 4781 + }, 4782 + { 4783 + "a": "ZH", 4784 + "b": "AA", 4785 + "id": "ZH_AA", 4786 + "weight": 0.8 4787 + }, 4788 + { 4789 + "a": "ZH", 4790 + "b": "AE", 4791 + "id": "ZH_AE", 4792 + "weight": 0.8 4793 + }, 4794 + { 4795 + "a": "ZH", 4796 + "b": "AH", 4797 + "id": "ZH_AH", 4798 + "weight": 0.8 4799 + }, 4800 + { 4801 + "a": "ZH", 4802 + "b": "AO", 4803 + "id": "ZH_AO", 4804 + "weight": 0.8 4805 + }, 4806 + { 4807 + "a": "ZH", 4808 + "b": "AW", 4809 + "id": "ZH_AW", 4810 + "weight": 0.8 4811 + }, 4812 + { 4813 + "a": "ZH", 4814 + "b": "AY", 4815 + "id": "ZH_AY", 4816 + "weight": 0.8 4817 + }, 4818 + { 4819 + "a": "ZH", 4820 + "b": "EH", 4821 + "id": "ZH_EH", 4822 + "weight": 0.8 4823 + }, 4824 + { 4825 + "a": "ZH", 4826 + "b": "ER", 4827 + "id": "ZH_ER", 4828 + "weight": 0.8 4829 + }, 4830 + { 4831 + "a": "ZH", 4832 + "b": "EY", 4833 + "id": "ZH_EY", 4834 + "weight": 0.8 4835 + }, 4836 + { 4837 + "a": "ZH", 4838 + "b": "IH", 4839 + "id": "ZH_IH", 4840 + "weight": 0.8 4841 + }, 4842 + { 4843 + "a": "ZH", 4844 + "b": "IY", 4845 + "id": "ZH_IY", 4846 + "weight": 0.8 4847 + }, 4848 + { 4849 + "a": "ZH", 4850 + "b": "OW", 4851 + "id": "ZH_OW", 4852 + "weight": 0.8 4853 + }, 4854 + { 4855 + "a": "ZH", 4856 + "b": "OY", 4857 + "id": "ZH_OY", 4858 + "weight": 0.8 4859 + }, 4860 + { 4861 + "a": "ZH", 4862 + "b": "UH", 4863 + "id": "ZH_UH", 4864 + "weight": 0.8 4865 + }, 4866 + { 4867 + "a": "ZH", 4868 + "b": "UW", 4869 + "id": "ZH_UW", 4870 + "weight": 0.8 4871 + }, 4872 + { 4873 + "a": "AA", 4874 + "b": "#E", 4875 + "id": "AA_#E", 4876 + "weight": 0.7 4877 + }, 4878 + { 4879 + "a": "AE", 4880 + "b": "#E", 4881 + "id": "AE_#E", 4882 + "weight": 0.7 4883 + }, 4884 + { 4885 + "a": "AH", 4886 + "b": "#E", 4887 + "id": "AH_#E", 4888 + "weight": 0.7 4889 + }, 4890 + { 4891 + "a": "AO", 4892 + "b": "#E", 4893 + "id": "AO_#E", 4894 + "weight": 0.7 4895 + }, 4896 + { 4897 + "a": "AW", 4898 + "b": "#E", 4899 + "id": "AW_#E", 4900 + "weight": 0.7 4901 + }, 4902 + { 4903 + "a": "AY", 4904 + "b": "#E", 4905 + "id": "AY_#E", 4906 + "weight": 0.7 4907 + }, 4908 + { 4909 + "a": "B", 4910 + "b": "#E", 4911 + "id": "B_#E", 4912 + "weight": 0.7 4913 + }, 4914 + { 4915 + "a": "CH", 4916 + "b": "#E", 4917 + "id": "CH_#E", 4918 + "weight": 0.7 4919 + }, 4920 + { 4921 + "a": "D", 4922 + "b": "#E", 4923 + "id": "D_#E", 4924 + "weight": 0.7 4925 + }, 4926 + { 4927 + "a": "DH", 4928 + "b": "#E", 4929 + "id": "DH_#E", 4930 + "weight": 0.7 4931 + }, 4932 + { 4933 + "a": "EH", 4934 + "b": "#E", 4935 + "id": "EH_#E", 4936 + "weight": 0.7 4937 + }, 4938 + { 4939 + "a": "ER", 4940 + "b": "#E", 4941 + "id": "ER_#E", 4942 + "weight": 0.7 4943 + }, 4944 + { 4945 + "a": "EY", 4946 + "b": "#E", 4947 + "id": "EY_#E", 4948 + "weight": 0.7 4949 + }, 4950 + { 4951 + "a": "F", 4952 + "b": "#E", 4953 + "id": "F_#E", 4954 + "weight": 0.7 4955 + }, 4956 + { 4957 + "a": "G", 4958 + "b": "#E", 4959 + "id": "G_#E", 4960 + "weight": 0.7 4961 + }, 4962 + { 4963 + "a": "IH", 4964 + "b": "#E", 4965 + "id": "IH_#E", 4966 + "weight": 0.7 4967 + }, 4968 + { 4969 + "a": "IY", 4970 + "b": "#E", 4971 + "id": "IY_#E", 4972 + "weight": 0.7 4973 + }, 4974 + { 4975 + "a": "JH", 4976 + "b": "#E", 4977 + "id": "JH_#E", 4978 + "weight": 0.7 4979 + }, 4980 + { 4981 + "a": "K", 4982 + "b": "#E", 4983 + "id": "K_#E", 4984 + "weight": 0.7 4985 + }, 4986 + { 4987 + "a": "L", 4988 + "b": "#E", 4989 + "id": "L_#E", 4990 + "weight": 0.7 4991 + }, 4992 + { 4993 + "a": "M", 4994 + "b": "#E", 4995 + "id": "M_#E", 4996 + "weight": 0.7 4997 + }, 4998 + { 4999 + "a": "N", 5000 + "b": "#E", 5001 + "id": "N_#E", 5002 + "weight": 0.7 5003 + }, 5004 + { 5005 + "a": "NG", 5006 + "b": "#E", 5007 + "id": "NG_#E", 5008 + "weight": 0.7 5009 + }, 5010 + { 5011 + "a": "OW", 5012 + "b": "#E", 5013 + "id": "OW_#E", 5014 + "weight": 0.7 5015 + }, 5016 + { 5017 + "a": "OY", 5018 + "b": "#E", 5019 + "id": "OY_#E", 5020 + "weight": 0.7 5021 + }, 5022 + { 5023 + "a": "P", 5024 + "b": "#E", 5025 + "id": "P_#E", 5026 + "weight": 0.7 5027 + }, 5028 + { 5029 + "a": "R", 5030 + "b": "#E", 5031 + "id": "R_#E", 5032 + "weight": 0.7 5033 + }, 5034 + { 5035 + "a": "S", 5036 + "b": "#E", 5037 + "id": "S_#E", 5038 + "weight": 0.7 5039 + }, 5040 + { 5041 + "a": "SH", 5042 + "b": "#E", 5043 + "id": "SH_#E", 5044 + "weight": 0.7 5045 + }, 5046 + { 5047 + "a": "T", 5048 + "b": "#E", 5049 + "id": "T_#E", 5050 + "weight": 0.7 5051 + }, 5052 + { 5053 + "a": "TH", 5054 + "b": "#E", 5055 + "id": "TH_#E", 5056 + "weight": 0.7 5057 + }, 5058 + { 5059 + "a": "UH", 5060 + "b": "#E", 5061 + "id": "UH_#E", 5062 + "weight": 0.7 5063 + }, 5064 + { 5065 + "a": "UW", 5066 + "b": "#E", 5067 + "id": "UW_#E", 5068 + "weight": 0.7 5069 + }, 5070 + { 5071 + "a": "V", 5072 + "b": "#E", 5073 + "id": "V_#E", 5074 + "weight": 0.7 5075 + }, 5076 + { 5077 + "a": "Z", 5078 + "b": "#E", 5079 + "id": "Z_#E", 5080 + "weight": 0.7 5081 + }, 5082 + { 5083 + "a": "ZH", 5084 + "b": "#E", 5085 + "id": "ZH_#E", 5086 + "weight": 0.7 5087 + }, 5088 + { 5089 + "a": "AA", 5090 + "b": "CH", 5091 + "id": "AA_CH", 5092 + "weight": 0.5 5093 + }, 5094 + { 5095 + "a": "AA", 5096 + "b": "JH", 5097 + "id": "AA_JH", 5098 + "weight": 0.5 5099 + }, 5100 + { 5101 + "a": "AA", 5102 + "b": "L", 5103 + "id": "AA_L", 5104 + "weight": 0.5 5105 + }, 5106 + { 5107 + "a": "AA", 5108 + "b": "R", 5109 + "id": "AA_R", 5110 + "weight": 0.5 5111 + }, 5112 + { 5113 + "a": "AA", 5114 + "b": "W", 5115 + "id": "AA_W", 5116 + "weight": 0.5 5117 + }, 5118 + { 5119 + "a": "AA", 5120 + "b": "Y", 5121 + "id": "AA_Y", 5122 + "weight": 0.5 5123 + }, 5124 + { 5125 + "a": "AE", 5126 + "b": "CH", 5127 + "id": "AE_CH", 5128 + "weight": 0.5 5129 + }, 5130 + { 5131 + "a": "AE", 5132 + "b": "JH", 5133 + "id": "AE_JH", 5134 + "weight": 0.5 5135 + }, 5136 + { 5137 + "a": "AE", 5138 + "b": "L", 5139 + "id": "AE_L", 5140 + "weight": 0.5 5141 + }, 5142 + { 5143 + "a": "AE", 5144 + "b": "R", 5145 + "id": "AE_R", 5146 + "weight": 0.5 5147 + }, 5148 + { 5149 + "a": "AE", 5150 + "b": "W", 5151 + "id": "AE_W", 5152 + "weight": 0.5 5153 + }, 5154 + { 5155 + "a": "AE", 5156 + "b": "Y", 5157 + "id": "AE_Y", 5158 + "weight": 0.5 5159 + }, 5160 + { 5161 + "a": "AH", 5162 + "b": "CH", 5163 + "id": "AH_CH", 5164 + "weight": 0.5 5165 + }, 5166 + { 5167 + "a": "AH", 5168 + "b": "JH", 5169 + "id": "AH_JH", 5170 + "weight": 0.5 5171 + }, 5172 + { 5173 + "a": "AH", 5174 + "b": "L", 5175 + "id": "AH_L", 5176 + "weight": 0.5 5177 + }, 5178 + { 5179 + "a": "AH", 5180 + "b": "R", 5181 + "id": "AH_R", 5182 + "weight": 0.5 5183 + }, 5184 + { 5185 + "a": "AH", 5186 + "b": "W", 5187 + "id": "AH_W", 5188 + "weight": 0.5 5189 + }, 5190 + { 5191 + "a": "AH", 5192 + "b": "Y", 5193 + "id": "AH_Y", 5194 + "weight": 0.5 5195 + }, 5196 + { 5197 + "a": "AO", 5198 + "b": "CH", 5199 + "id": "AO_CH", 5200 + "weight": 0.5 5201 + }, 5202 + { 5203 + "a": "AO", 5204 + "b": "JH", 5205 + "id": "AO_JH", 5206 + "weight": 0.5 5207 + }, 5208 + { 5209 + "a": "AO", 5210 + "b": "L", 5211 + "id": "AO_L", 5212 + "weight": 0.5 5213 + }, 5214 + { 5215 + "a": "AO", 5216 + "b": "R", 5217 + "id": "AO_R", 5218 + "weight": 0.5 5219 + }, 5220 + { 5221 + "a": "AO", 5222 + "b": "W", 5223 + "id": "AO_W", 5224 + "weight": 0.5 5225 + }, 5226 + { 5227 + "a": "AO", 5228 + "b": "Y", 5229 + "id": "AO_Y", 5230 + "weight": 0.5 5231 + }, 5232 + { 5233 + "a": "AW", 5234 + "b": "CH", 5235 + "id": "AW_CH", 5236 + "weight": 0.5 5237 + }, 5238 + { 5239 + "a": "AW", 5240 + "b": "JH", 5241 + "id": "AW_JH", 5242 + "weight": 0.5 5243 + }, 5244 + { 5245 + "a": "AW", 5246 + "b": "L", 5247 + "id": "AW_L", 5248 + "weight": 0.5 5249 + }, 5250 + { 5251 + "a": "AW", 5252 + "b": "R", 5253 + "id": "AW_R", 5254 + "weight": 0.5 5255 + }, 5256 + { 5257 + "a": "AW", 5258 + "b": "W", 5259 + "id": "AW_W", 5260 + "weight": 0.5 5261 + }, 5262 + { 5263 + "a": "AW", 5264 + "b": "Y", 5265 + "id": "AW_Y", 5266 + "weight": 0.5 5267 + }, 5268 + { 5269 + "a": "AY", 5270 + "b": "CH", 5271 + "id": "AY_CH", 5272 + "weight": 0.5 5273 + }, 5274 + { 5275 + "a": "AY", 5276 + "b": "JH", 5277 + "id": "AY_JH", 5278 + "weight": 0.5 5279 + }, 5280 + { 5281 + "a": "AY", 5282 + "b": "L", 5283 + "id": "AY_L", 5284 + "weight": 0.5 5285 + }, 5286 + { 5287 + "a": "AY", 5288 + "b": "R", 5289 + "id": "AY_R", 5290 + "weight": 0.5 5291 + }, 5292 + { 5293 + "a": "AY", 5294 + "b": "W", 5295 + "id": "AY_W", 5296 + "weight": 0.5 5297 + }, 5298 + { 5299 + "a": "AY", 5300 + "b": "Y", 5301 + "id": "AY_Y", 5302 + "weight": 0.5 5303 + }, 5304 + { 5305 + "a": "B", 5306 + "b": "B", 5307 + "id": "B_B", 5308 + "weight": 0.5 5309 + }, 5310 + { 5311 + "a": "B", 5312 + "b": "CH", 5313 + "id": "B_CH", 5314 + "weight": 0.5 5315 + }, 5316 + { 5317 + "a": "B", 5318 + "b": "D", 5319 + "id": "B_D", 5320 + "weight": 0.5 5321 + }, 5322 + { 5323 + "a": "B", 5324 + "b": "DH", 5325 + "id": "B_DH", 5326 + "weight": 0.5 5327 + }, 5328 + { 5329 + "a": "B", 5330 + "b": "F", 5331 + "id": "B_F", 5332 + "weight": 0.5 5333 + }, 5334 + { 5335 + "a": "B", 5336 + "b": "G", 5337 + "id": "B_G", 5338 + "weight": 0.5 5339 + }, 5340 + { 5341 + "a": "B", 5342 + "b": "JH", 5343 + "id": "B_JH", 5344 + "weight": 0.5 5345 + }, 5346 + { 5347 + "a": "B", 5348 + "b": "K", 5349 + "id": "B_K", 5350 + "weight": 0.5 5351 + }, 5352 + { 5353 + "a": "B", 5354 + "b": "L", 5355 + "id": "B_L", 5356 + "weight": 0.5 5357 + }, 5358 + { 5359 + "a": "B", 5360 + "b": "M", 5361 + "id": "B_M", 5362 + "weight": 0.5 5363 + }, 5364 + { 5365 + "a": "B", 5366 + "b": "N", 5367 + "id": "B_N", 5368 + "weight": 0.5 5369 + }, 5370 + { 5371 + "a": "B", 5372 + "b": "NG", 5373 + "id": "B_NG", 5374 + "weight": 0.5 5375 + }, 5376 + { 5377 + "a": "B", 5378 + "b": "P", 5379 + "id": "B_P", 5380 + "weight": 0.5 5381 + }, 5382 + { 5383 + "a": "B", 5384 + "b": "R", 5385 + "id": "B_R", 5386 + "weight": 0.5 5387 + }, 5388 + { 5389 + "a": "B", 5390 + "b": "S", 5391 + "id": "B_S", 5392 + "weight": 0.5 5393 + }, 5394 + { 5395 + "a": "B", 5396 + "b": "SH", 5397 + "id": "B_SH", 5398 + "weight": 0.5 5399 + }, 5400 + { 5401 + "a": "B", 5402 + "b": "T", 5403 + "id": "B_T", 5404 + "weight": 0.5 5405 + }, 5406 + { 5407 + "a": "B", 5408 + "b": "TH", 5409 + "id": "B_TH", 5410 + "weight": 0.5 5411 + }, 5412 + { 5413 + "a": "B", 5414 + "b": "V", 5415 + "id": "B_V", 5416 + "weight": 0.5 5417 + }, 5418 + { 5419 + "a": "B", 5420 + "b": "W", 5421 + "id": "B_W", 5422 + "weight": 0.5 5423 + }, 5424 + { 5425 + "a": "B", 5426 + "b": "Y", 5427 + "id": "B_Y", 5428 + "weight": 0.5 5429 + }, 5430 + { 5431 + "a": "B", 5432 + "b": "Z", 5433 + "id": "B_Z", 5434 + "weight": 0.5 5435 + }, 5436 + { 5437 + "a": "CH", 5438 + "b": "AA", 5439 + "id": "CH_AA", 5440 + "weight": 0.5 5441 + }, 5442 + { 5443 + "a": "CH", 5444 + "b": "AE", 5445 + "id": "CH_AE", 5446 + "weight": 0.5 5447 + }, 5448 + { 5449 + "a": "CH", 5450 + "b": "AH", 5451 + "id": "CH_AH", 5452 + "weight": 0.5 5453 + }, 5454 + { 5455 + "a": "CH", 5456 + "b": "AO", 5457 + "id": "CH_AO", 5458 + "weight": 0.5 5459 + }, 5460 + { 5461 + "a": "CH", 5462 + "b": "AW", 5463 + "id": "CH_AW", 5464 + "weight": 0.5 5465 + }, 5466 + { 5467 + "a": "CH", 5468 + "b": "AY", 5469 + "id": "CH_AY", 5470 + "weight": 0.5 5471 + }, 5472 + { 5473 + "a": "CH", 5474 + "b": "B", 5475 + "id": "CH_B", 5476 + "weight": 0.5 5477 + }, 5478 + { 5479 + "a": "CH", 5480 + "b": "CH", 5481 + "id": "CH_CH", 5482 + "weight": 0.5 5483 + }, 5484 + { 5485 + "a": "CH", 5486 + "b": "D", 5487 + "id": "CH_D", 5488 + "weight": 0.5 5489 + }, 5490 + { 5491 + "a": "CH", 5492 + "b": "DH", 5493 + "id": "CH_DH", 5494 + "weight": 0.5 5495 + }, 5496 + { 5497 + "a": "CH", 5498 + "b": "EH", 5499 + "id": "CH_EH", 5500 + "weight": 0.5 5501 + }, 5502 + { 5503 + "a": "CH", 5504 + "b": "ER", 5505 + "id": "CH_ER", 5506 + "weight": 0.5 5507 + }, 5508 + { 5509 + "a": "CH", 5510 + "b": "EY", 5511 + "id": "CH_EY", 5512 + "weight": 0.5 5513 + }, 5514 + { 5515 + "a": "CH", 5516 + "b": "F", 5517 + "id": "CH_F", 5518 + "weight": 0.5 5519 + }, 5520 + { 5521 + "a": "CH", 5522 + "b": "G", 5523 + "id": "CH_G", 5524 + "weight": 0.5 5525 + }, 5526 + { 5527 + "a": "CH", 5528 + "b": "IH", 5529 + "id": "CH_IH", 5530 + "weight": 0.5 5531 + }, 5532 + { 5533 + "a": "CH", 5534 + "b": "IY", 5535 + "id": "CH_IY", 5536 + "weight": 0.5 5537 + }, 5538 + { 5539 + "a": "CH", 5540 + "b": "JH", 5541 + "id": "CH_JH", 5542 + "weight": 0.5 5543 + }, 5544 + { 5545 + "a": "CH", 5546 + "b": "K", 5547 + "id": "CH_K", 5548 + "weight": 0.5 5549 + }, 5550 + { 5551 + "a": "CH", 5552 + "b": "L", 5553 + "id": "CH_L", 5554 + "weight": 0.5 5555 + }, 5556 + { 5557 + "a": "CH", 5558 + "b": "M", 5559 + "id": "CH_M", 5560 + "weight": 0.5 5561 + }, 5562 + { 5563 + "a": "CH", 5564 + "b": "N", 5565 + "id": "CH_N", 5566 + "weight": 0.5 5567 + }, 5568 + { 5569 + "a": "CH", 5570 + "b": "NG", 5571 + "id": "CH_NG", 5572 + "weight": 0.5 5573 + }, 5574 + { 5575 + "a": "CH", 5576 + "b": "OW", 5577 + "id": "CH_OW", 5578 + "weight": 0.5 5579 + }, 5580 + { 5581 + "a": "CH", 5582 + "b": "OY", 5583 + "id": "CH_OY", 5584 + "weight": 0.5 5585 + }, 5586 + { 5587 + "a": "CH", 5588 + "b": "P", 5589 + "id": "CH_P", 5590 + "weight": 0.5 5591 + }, 5592 + { 5593 + "a": "CH", 5594 + "b": "R", 5595 + "id": "CH_R", 5596 + "weight": 0.5 5597 + }, 5598 + { 5599 + "a": "CH", 5600 + "b": "S", 5601 + "id": "CH_S", 5602 + "weight": 0.5 5603 + }, 5604 + { 5605 + "a": "CH", 5606 + "b": "SH", 5607 + "id": "CH_SH", 5608 + "weight": 0.5 5609 + }, 5610 + { 5611 + "a": "CH", 5612 + "b": "T", 5613 + "id": "CH_T", 5614 + "weight": 0.5 5615 + }, 5616 + { 5617 + "a": "CH", 5618 + "b": "TH", 5619 + "id": "CH_TH", 5620 + "weight": 0.5 5621 + }, 5622 + { 5623 + "a": "CH", 5624 + "b": "UH", 5625 + "id": "CH_UH", 5626 + "weight": 0.5 5627 + }, 5628 + { 5629 + "a": "CH", 5630 + "b": "UW", 5631 + "id": "CH_UW", 5632 + "weight": 0.5 5633 + }, 5634 + { 5635 + "a": "CH", 5636 + "b": "V", 5637 + "id": "CH_V", 5638 + "weight": 0.5 5639 + }, 5640 + { 5641 + "a": "CH", 5642 + "b": "W", 5643 + "id": "CH_W", 5644 + "weight": 0.5 5645 + }, 5646 + { 5647 + "a": "CH", 5648 + "b": "Y", 5649 + "id": "CH_Y", 5650 + "weight": 0.5 5651 + }, 5652 + { 5653 + "a": "CH", 5654 + "b": "Z", 5655 + "id": "CH_Z", 5656 + "weight": 0.5 5657 + }, 5658 + { 5659 + "a": "D", 5660 + "b": "B", 5661 + "id": "D_B", 5662 + "weight": 0.5 5663 + }, 5664 + { 5665 + "a": "D", 5666 + "b": "CH", 5667 + "id": "D_CH", 5668 + "weight": 0.5 5669 + }, 5670 + { 5671 + "a": "D", 5672 + "b": "D", 5673 + "id": "D_D", 5674 + "weight": 0.5 5675 + }, 5676 + { 5677 + "a": "D", 5678 + "b": "DH", 5679 + "id": "D_DH", 5680 + "weight": 0.5 5681 + }, 5682 + { 5683 + "a": "D", 5684 + "b": "F", 5685 + "id": "D_F", 5686 + "weight": 0.5 5687 + }, 5688 + { 5689 + "a": "D", 5690 + "b": "G", 5691 + "id": "D_G", 5692 + "weight": 0.5 5693 + }, 5694 + { 5695 + "a": "D", 5696 + "b": "JH", 5697 + "id": "D_JH", 5698 + "weight": 0.5 5699 + }, 5700 + { 5701 + "a": "D", 5702 + "b": "K", 5703 + "id": "D_K", 5704 + "weight": 0.5 5705 + }, 5706 + { 5707 + "a": "D", 5708 + "b": "L", 5709 + "id": "D_L", 5710 + "weight": 0.5 5711 + }, 5712 + { 5713 + "a": "D", 5714 + "b": "M", 5715 + "id": "D_M", 5716 + "weight": 0.5 5717 + }, 5718 + { 5719 + "a": "D", 5720 + "b": "N", 5721 + "id": "D_N", 5722 + "weight": 0.5 5723 + }, 5724 + { 5725 + "a": "D", 5726 + "b": "NG", 5727 + "id": "D_NG", 5728 + "weight": 0.5 5729 + }, 5730 + { 5731 + "a": "D", 5732 + "b": "P", 5733 + "id": "D_P", 5734 + "weight": 0.5 5735 + }, 5736 + { 5737 + "a": "D", 5738 + "b": "R", 5739 + "id": "D_R", 5740 + "weight": 0.5 5741 + }, 5742 + { 5743 + "a": "D", 5744 + "b": "S", 5745 + "id": "D_S", 5746 + "weight": 0.5 5747 + }, 5748 + { 5749 + "a": "D", 5750 + "b": "SH", 5751 + "id": "D_SH", 5752 + "weight": 0.5 5753 + }, 5754 + { 5755 + "a": "D", 5756 + "b": "T", 5757 + "id": "D_T", 5758 + "weight": 0.5 5759 + }, 5760 + { 5761 + "a": "D", 5762 + "b": "TH", 5763 + "id": "D_TH", 5764 + "weight": 0.5 5765 + }, 5766 + { 5767 + "a": "D", 5768 + "b": "V", 5769 + "id": "D_V", 5770 + "weight": 0.5 5771 + }, 5772 + { 5773 + "a": "D", 5774 + "b": "W", 5775 + "id": "D_W", 5776 + "weight": 0.5 5777 + }, 5778 + { 5779 + "a": "D", 5780 + "b": "Y", 5781 + "id": "D_Y", 5782 + "weight": 0.5 5783 + }, 5784 + { 5785 + "a": "D", 5786 + "b": "Z", 5787 + "id": "D_Z", 5788 + "weight": 0.5 5789 + }, 5790 + { 5791 + "a": "DH", 5792 + "b": "B", 5793 + "id": "DH_B", 5794 + "weight": 0.5 5795 + }, 5796 + { 5797 + "a": "DH", 5798 + "b": "CH", 5799 + "id": "DH_CH", 5800 + "weight": 0.5 5801 + }, 5802 + { 5803 + "a": "DH", 5804 + "b": "D", 5805 + "id": "DH_D", 5806 + "weight": 0.5 5807 + }, 5808 + { 5809 + "a": "DH", 5810 + "b": "DH", 5811 + "id": "DH_DH", 5812 + "weight": 0.5 5813 + }, 5814 + { 5815 + "a": "DH", 5816 + "b": "F", 5817 + "id": "DH_F", 5818 + "weight": 0.5 5819 + }, 5820 + { 5821 + "a": "DH", 5822 + "b": "G", 5823 + "id": "DH_G", 5824 + "weight": 0.5 5825 + }, 5826 + { 5827 + "a": "DH", 5828 + "b": "JH", 5829 + "id": "DH_JH", 5830 + "weight": 0.5 5831 + }, 5832 + { 5833 + "a": "DH", 5834 + "b": "K", 5835 + "id": "DH_K", 5836 + "weight": 0.5 5837 + }, 5838 + { 5839 + "a": "DH", 5840 + "b": "L", 5841 + "id": "DH_L", 5842 + "weight": 0.5 5843 + }, 5844 + { 5845 + "a": "DH", 5846 + "b": "M", 5847 + "id": "DH_M", 5848 + "weight": 0.5 5849 + }, 5850 + { 5851 + "a": "DH", 5852 + "b": "N", 5853 + "id": "DH_N", 5854 + "weight": 0.5 5855 + }, 5856 + { 5857 + "a": "DH", 5858 + "b": "NG", 5859 + "id": "DH_NG", 5860 + "weight": 0.5 5861 + }, 5862 + { 5863 + "a": "DH", 5864 + "b": "P", 5865 + "id": "DH_P", 5866 + "weight": 0.5 5867 + }, 5868 + { 5869 + "a": "DH", 5870 + "b": "R", 5871 + "id": "DH_R", 5872 + "weight": 0.5 5873 + }, 5874 + { 5875 + "a": "DH", 5876 + "b": "S", 5877 + "id": "DH_S", 5878 + "weight": 0.5 5879 + }, 5880 + { 5881 + "a": "DH", 5882 + "b": "SH", 5883 + "id": "DH_SH", 5884 + "weight": 0.5 5885 + }, 5886 + { 5887 + "a": "DH", 5888 + "b": "T", 5889 + "id": "DH_T", 5890 + "weight": 0.5 5891 + }, 5892 + { 5893 + "a": "DH", 5894 + "b": "TH", 5895 + "id": "DH_TH", 5896 + "weight": 0.5 5897 + }, 5898 + { 5899 + "a": "DH", 5900 + "b": "V", 5901 + "id": "DH_V", 5902 + "weight": 0.5 5903 + }, 5904 + { 5905 + "a": "DH", 5906 + "b": "W", 5907 + "id": "DH_W", 5908 + "weight": 0.5 5909 + }, 5910 + { 5911 + "a": "DH", 5912 + "b": "Y", 5913 + "id": "DH_Y", 5914 + "weight": 0.5 5915 + }, 5916 + { 5917 + "a": "DH", 5918 + "b": "Z", 5919 + "id": "DH_Z", 5920 + "weight": 0.5 5921 + }, 5922 + { 5923 + "a": "EH", 5924 + "b": "CH", 5925 + "id": "EH_CH", 5926 + "weight": 0.5 5927 + }, 5928 + { 5929 + "a": "EH", 5930 + "b": "JH", 5931 + "id": "EH_JH", 5932 + "weight": 0.5 5933 + }, 5934 + { 5935 + "a": "EH", 5936 + "b": "L", 5937 + "id": "EH_L", 5938 + "weight": 0.5 5939 + }, 5940 + { 5941 + "a": "EH", 5942 + "b": "R", 5943 + "id": "EH_R", 5944 + "weight": 0.5 5945 + }, 5946 + { 5947 + "a": "EH", 5948 + "b": "W", 5949 + "id": "EH_W", 5950 + "weight": 0.5 5951 + }, 5952 + { 5953 + "a": "EH", 5954 + "b": "Y", 5955 + "id": "EH_Y", 5956 + "weight": 0.5 5957 + }, 5958 + { 5959 + "a": "ER", 5960 + "b": "CH", 5961 + "id": "ER_CH", 5962 + "weight": 0.5 5963 + }, 5964 + { 5965 + "a": "ER", 5966 + "b": "JH", 5967 + "id": "ER_JH", 5968 + "weight": 0.5 5969 + }, 5970 + { 5971 + "a": "ER", 5972 + "b": "L", 5973 + "id": "ER_L", 5974 + "weight": 0.5 5975 + }, 5976 + { 5977 + "a": "ER", 5978 + "b": "R", 5979 + "id": "ER_R", 5980 + "weight": 0.5 5981 + }, 5982 + { 5983 + "a": "ER", 5984 + "b": "W", 5985 + "id": "ER_W", 5986 + "weight": 0.5 5987 + }, 5988 + { 5989 + "a": "ER", 5990 + "b": "Y", 5991 + "id": "ER_Y", 5992 + "weight": 0.5 5993 + }, 5994 + { 5995 + "a": "EY", 5996 + "b": "CH", 5997 + "id": "EY_CH", 5998 + "weight": 0.5 5999 + }, 6000 + { 6001 + "a": "EY", 6002 + "b": "JH", 6003 + "id": "EY_JH", 6004 + "weight": 0.5 6005 + }, 6006 + { 6007 + "a": "EY", 6008 + "b": "L", 6009 + "id": "EY_L", 6010 + "weight": 0.5 6011 + }, 6012 + { 6013 + "a": "EY", 6014 + "b": "R", 6015 + "id": "EY_R", 6016 + "weight": 0.5 6017 + }, 6018 + { 6019 + "a": "EY", 6020 + "b": "W", 6021 + "id": "EY_W", 6022 + "weight": 0.5 6023 + }, 6024 + { 6025 + "a": "EY", 6026 + "b": "Y", 6027 + "id": "EY_Y", 6028 + "weight": 0.5 6029 + }, 6030 + { 6031 + "a": "F", 6032 + "b": "B", 6033 + "id": "F_B", 6034 + "weight": 0.5 6035 + }, 6036 + { 6037 + "a": "F", 6038 + "b": "CH", 6039 + "id": "F_CH", 6040 + "weight": 0.5 6041 + }, 6042 + { 6043 + "a": "F", 6044 + "b": "D", 6045 + "id": "F_D", 6046 + "weight": 0.5 6047 + }, 6048 + { 6049 + "a": "F", 6050 + "b": "DH", 6051 + "id": "F_DH", 6052 + "weight": 0.5 6053 + }, 6054 + { 6055 + "a": "F", 6056 + "b": "F", 6057 + "id": "F_F", 6058 + "weight": 0.5 6059 + }, 6060 + { 6061 + "a": "F", 6062 + "b": "G", 6063 + "id": "F_G", 6064 + "weight": 0.5 6065 + }, 6066 + { 6067 + "a": "F", 6068 + "b": "JH", 6069 + "id": "F_JH", 6070 + "weight": 0.5 6071 + }, 6072 + { 6073 + "a": "F", 6074 + "b": "K", 6075 + "id": "F_K", 6076 + "weight": 0.5 6077 + }, 6078 + { 6079 + "a": "F", 6080 + "b": "L", 6081 + "id": "F_L", 6082 + "weight": 0.5 6083 + }, 6084 + { 6085 + "a": "F", 6086 + "b": "M", 6087 + "id": "F_M", 6088 + "weight": 0.5 6089 + }, 6090 + { 6091 + "a": "F", 6092 + "b": "N", 6093 + "id": "F_N", 6094 + "weight": 0.5 6095 + }, 6096 + { 6097 + "a": "F", 6098 + "b": "NG", 6099 + "id": "F_NG", 6100 + "weight": 0.5 6101 + }, 6102 + { 6103 + "a": "F", 6104 + "b": "P", 6105 + "id": "F_P", 6106 + "weight": 0.5 6107 + }, 6108 + { 6109 + "a": "F", 6110 + "b": "R", 6111 + "id": "F_R", 6112 + "weight": 0.5 6113 + }, 6114 + { 6115 + "a": "F", 6116 + "b": "S", 6117 + "id": "F_S", 6118 + "weight": 0.5 6119 + }, 6120 + { 6121 + "a": "F", 6122 + "b": "SH", 6123 + "id": "F_SH", 6124 + "weight": 0.5 6125 + }, 6126 + { 6127 + "a": "F", 6128 + "b": "T", 6129 + "id": "F_T", 6130 + "weight": 0.5 6131 + }, 6132 + { 6133 + "a": "F", 6134 + "b": "TH", 6135 + "id": "F_TH", 6136 + "weight": 0.5 6137 + }, 6138 + { 6139 + "a": "F", 6140 + "b": "V", 6141 + "id": "F_V", 6142 + "weight": 0.5 6143 + }, 6144 + { 6145 + "a": "F", 6146 + "b": "W", 6147 + "id": "F_W", 6148 + "weight": 0.5 6149 + }, 6150 + { 6151 + "a": "F", 6152 + "b": "Y", 6153 + "id": "F_Y", 6154 + "weight": 0.5 6155 + }, 6156 + { 6157 + "a": "F", 6158 + "b": "Z", 6159 + "id": "F_Z", 6160 + "weight": 0.5 6161 + }, 6162 + { 6163 + "a": "G", 6164 + "b": "B", 6165 + "id": "G_B", 6166 + "weight": 0.5 6167 + }, 6168 + { 6169 + "a": "G", 6170 + "b": "CH", 6171 + "id": "G_CH", 6172 + "weight": 0.5 6173 + }, 6174 + { 6175 + "a": "G", 6176 + "b": "D", 6177 + "id": "G_D", 6178 + "weight": 0.5 6179 + }, 6180 + { 6181 + "a": "G", 6182 + "b": "DH", 6183 + "id": "G_DH", 6184 + "weight": 0.5 6185 + }, 6186 + { 6187 + "a": "G", 6188 + "b": "F", 6189 + "id": "G_F", 6190 + "weight": 0.5 6191 + }, 6192 + { 6193 + "a": "G", 6194 + "b": "G", 6195 + "id": "G_G", 6196 + "weight": 0.5 6197 + }, 6198 + { 6199 + "a": "G", 6200 + "b": "JH", 6201 + "id": "G_JH", 6202 + "weight": 0.5 6203 + }, 6204 + { 6205 + "a": "G", 6206 + "b": "K", 6207 + "id": "G_K", 6208 + "weight": 0.5 6209 + }, 6210 + { 6211 + "a": "G", 6212 + "b": "L", 6213 + "id": "G_L", 6214 + "weight": 0.5 6215 + }, 6216 + { 6217 + "a": "G", 6218 + "b": "M", 6219 + "id": "G_M", 6220 + "weight": 0.5 6221 + }, 6222 + { 6223 + "a": "G", 6224 + "b": "N", 6225 + "id": "G_N", 6226 + "weight": 0.5 6227 + }, 6228 + { 6229 + "a": "G", 6230 + "b": "NG", 6231 + "id": "G_NG", 6232 + "weight": 0.5 6233 + }, 6234 + { 6235 + "a": "G", 6236 + "b": "P", 6237 + "id": "G_P", 6238 + "weight": 0.5 6239 + }, 6240 + { 6241 + "a": "G", 6242 + "b": "R", 6243 + "id": "G_R", 6244 + "weight": 0.5 6245 + }, 6246 + { 6247 + "a": "G", 6248 + "b": "S", 6249 + "id": "G_S", 6250 + "weight": 0.5 6251 + }, 6252 + { 6253 + "a": "G", 6254 + "b": "SH", 6255 + "id": "G_SH", 6256 + "weight": 0.5 6257 + }, 6258 + { 6259 + "a": "G", 6260 + "b": "T", 6261 + "id": "G_T", 6262 + "weight": 0.5 6263 + }, 6264 + { 6265 + "a": "G", 6266 + "b": "TH", 6267 + "id": "G_TH", 6268 + "weight": 0.5 6269 + }, 6270 + { 6271 + "a": "G", 6272 + "b": "V", 6273 + "id": "G_V", 6274 + "weight": 0.5 6275 + }, 6276 + { 6277 + "a": "G", 6278 + "b": "W", 6279 + "id": "G_W", 6280 + "weight": 0.5 6281 + }, 6282 + { 6283 + "a": "G", 6284 + "b": "Y", 6285 + "id": "G_Y", 6286 + "weight": 0.5 6287 + }, 6288 + { 6289 + "a": "G", 6290 + "b": "Z", 6291 + "id": "G_Z", 6292 + "weight": 0.5 6293 + }, 6294 + { 6295 + "a": "HH", 6296 + "b": "B", 6297 + "id": "HH_B", 6298 + "weight": 0.5 6299 + }, 6300 + { 6301 + "a": "HH", 6302 + "b": "CH", 6303 + "id": "HH_CH", 6304 + "weight": 0.5 6305 + }, 6306 + { 6307 + "a": "HH", 6308 + "b": "D", 6309 + "id": "HH_D", 6310 + "weight": 0.5 6311 + }, 6312 + { 6313 + "a": "HH", 6314 + "b": "DH", 6315 + "id": "HH_DH", 6316 + "weight": 0.5 6317 + }, 6318 + { 6319 + "a": "HH", 6320 + "b": "F", 6321 + "id": "HH_F", 6322 + "weight": 0.5 6323 + }, 6324 + { 6325 + "a": "HH", 6326 + "b": "G", 6327 + "id": "HH_G", 6328 + "weight": 0.5 6329 + }, 6330 + { 6331 + "a": "HH", 6332 + "b": "JH", 6333 + "id": "HH_JH", 6334 + "weight": 0.5 6335 + }, 6336 + { 6337 + "a": "HH", 6338 + "b": "K", 6339 + "id": "HH_K", 6340 + "weight": 0.5 6341 + }, 6342 + { 6343 + "a": "HH", 6344 + "b": "L", 6345 + "id": "HH_L", 6346 + "weight": 0.5 6347 + }, 6348 + { 6349 + "a": "HH", 6350 + "b": "M", 6351 + "id": "HH_M", 6352 + "weight": 0.5 6353 + }, 6354 + { 6355 + "a": "HH", 6356 + "b": "N", 6357 + "id": "HH_N", 6358 + "weight": 0.5 6359 + }, 6360 + { 6361 + "a": "HH", 6362 + "b": "NG", 6363 + "id": "HH_NG", 6364 + "weight": 0.5 6365 + }, 6366 + { 6367 + "a": "HH", 6368 + "b": "P", 6369 + "id": "HH_P", 6370 + "weight": 0.5 6371 + }, 6372 + { 6373 + "a": "HH", 6374 + "b": "R", 6375 + "id": "HH_R", 6376 + "weight": 0.5 6377 + }, 6378 + { 6379 + "a": "HH", 6380 + "b": "S", 6381 + "id": "HH_S", 6382 + "weight": 0.5 6383 + }, 6384 + { 6385 + "a": "HH", 6386 + "b": "SH", 6387 + "id": "HH_SH", 6388 + "weight": 0.5 6389 + }, 6390 + { 6391 + "a": "HH", 6392 + "b": "T", 6393 + "id": "HH_T", 6394 + "weight": 0.5 6395 + }, 6396 + { 6397 + "a": "HH", 6398 + "b": "TH", 6399 + "id": "HH_TH", 6400 + "weight": 0.5 6401 + }, 6402 + { 6403 + "a": "HH", 6404 + "b": "V", 6405 + "id": "HH_V", 6406 + "weight": 0.5 6407 + }, 6408 + { 6409 + "a": "HH", 6410 + "b": "W", 6411 + "id": "HH_W", 6412 + "weight": 0.5 6413 + }, 6414 + { 6415 + "a": "HH", 6416 + "b": "Y", 6417 + "id": "HH_Y", 6418 + "weight": 0.5 6419 + }, 6420 + { 6421 + "a": "HH", 6422 + "b": "Z", 6423 + "id": "HH_Z", 6424 + "weight": 0.5 6425 + }, 6426 + { 6427 + "a": "IH", 6428 + "b": "CH", 6429 + "id": "IH_CH", 6430 + "weight": 0.5 6431 + }, 6432 + { 6433 + "a": "IH", 6434 + "b": "JH", 6435 + "id": "IH_JH", 6436 + "weight": 0.5 6437 + }, 6438 + { 6439 + "a": "IH", 6440 + "b": "L", 6441 + "id": "IH_L", 6442 + "weight": 0.5 6443 + }, 6444 + { 6445 + "a": "IH", 6446 + "b": "R", 6447 + "id": "IH_R", 6448 + "weight": 0.5 6449 + }, 6450 + { 6451 + "a": "IH", 6452 + "b": "W", 6453 + "id": "IH_W", 6454 + "weight": 0.5 6455 + }, 6456 + { 6457 + "a": "IH", 6458 + "b": "Y", 6459 + "id": "IH_Y", 6460 + "weight": 0.5 6461 + }, 6462 + { 6463 + "a": "IY", 6464 + "b": "CH", 6465 + "id": "IY_CH", 6466 + "weight": 0.5 6467 + }, 6468 + { 6469 + "a": "IY", 6470 + "b": "JH", 6471 + "id": "IY_JH", 6472 + "weight": 0.5 6473 + }, 6474 + { 6475 + "a": "IY", 6476 + "b": "L", 6477 + "id": "IY_L", 6478 + "weight": 0.5 6479 + }, 6480 + { 6481 + "a": "IY", 6482 + "b": "R", 6483 + "id": "IY_R", 6484 + "weight": 0.5 6485 + }, 6486 + { 6487 + "a": "IY", 6488 + "b": "W", 6489 + "id": "IY_W", 6490 + "weight": 0.5 6491 + }, 6492 + { 6493 + "a": "IY", 6494 + "b": "Y", 6495 + "id": "IY_Y", 6496 + "weight": 0.5 6497 + }, 6498 + { 6499 + "a": "JH", 6500 + "b": "AA", 6501 + "id": "JH_AA", 6502 + "weight": 0.5 6503 + }, 6504 + { 6505 + "a": "JH", 6506 + "b": "AE", 6507 + "id": "JH_AE", 6508 + "weight": 0.5 6509 + }, 6510 + { 6511 + "a": "JH", 6512 + "b": "AH", 6513 + "id": "JH_AH", 6514 + "weight": 0.5 6515 + }, 6516 + { 6517 + "a": "JH", 6518 + "b": "AO", 6519 + "id": "JH_AO", 6520 + "weight": 0.5 6521 + }, 6522 + { 6523 + "a": "JH", 6524 + "b": "AW", 6525 + "id": "JH_AW", 6526 + "weight": 0.5 6527 + }, 6528 + { 6529 + "a": "JH", 6530 + "b": "AY", 6531 + "id": "JH_AY", 6532 + "weight": 0.5 6533 + }, 6534 + { 6535 + "a": "JH", 6536 + "b": "B", 6537 + "id": "JH_B", 6538 + "weight": 0.5 6539 + }, 6540 + { 6541 + "a": "JH", 6542 + "b": "CH", 6543 + "id": "JH_CH", 6544 + "weight": 0.5 6545 + }, 6546 + { 6547 + "a": "JH", 6548 + "b": "D", 6549 + "id": "JH_D", 6550 + "weight": 0.5 6551 + }, 6552 + { 6553 + "a": "JH", 6554 + "b": "DH", 6555 + "id": "JH_DH", 6556 + "weight": 0.5 6557 + }, 6558 + { 6559 + "a": "JH", 6560 + "b": "EH", 6561 + "id": "JH_EH", 6562 + "weight": 0.5 6563 + }, 6564 + { 6565 + "a": "JH", 6566 + "b": "ER", 6567 + "id": "JH_ER", 6568 + "weight": 0.5 6569 + }, 6570 + { 6571 + "a": "JH", 6572 + "b": "EY", 6573 + "id": "JH_EY", 6574 + "weight": 0.5 6575 + }, 6576 + { 6577 + "a": "JH", 6578 + "b": "F", 6579 + "id": "JH_F", 6580 + "weight": 0.5 6581 + }, 6582 + { 6583 + "a": "JH", 6584 + "b": "G", 6585 + "id": "JH_G", 6586 + "weight": 0.5 6587 + }, 6588 + { 6589 + "a": "JH", 6590 + "b": "IH", 6591 + "id": "JH_IH", 6592 + "weight": 0.5 6593 + }, 6594 + { 6595 + "a": "JH", 6596 + "b": "IY", 6597 + "id": "JH_IY", 6598 + "weight": 0.5 6599 + }, 6600 + { 6601 + "a": "JH", 6602 + "b": "JH", 6603 + "id": "JH_JH", 6604 + "weight": 0.5 6605 + }, 6606 + { 6607 + "a": "JH", 6608 + "b": "K", 6609 + "id": "JH_K", 6610 + "weight": 0.5 6611 + }, 6612 + { 6613 + "a": "JH", 6614 + "b": "L", 6615 + "id": "JH_L", 6616 + "weight": 0.5 6617 + }, 6618 + { 6619 + "a": "JH", 6620 + "b": "M", 6621 + "id": "JH_M", 6622 + "weight": 0.5 6623 + }, 6624 + { 6625 + "a": "JH", 6626 + "b": "N", 6627 + "id": "JH_N", 6628 + "weight": 0.5 6629 + }, 6630 + { 6631 + "a": "JH", 6632 + "b": "NG", 6633 + "id": "JH_NG", 6634 + "weight": 0.5 6635 + }, 6636 + { 6637 + "a": "JH", 6638 + "b": "OW", 6639 + "id": "JH_OW", 6640 + "weight": 0.5 6641 + }, 6642 + { 6643 + "a": "JH", 6644 + "b": "OY", 6645 + "id": "JH_OY", 6646 + "weight": 0.5 6647 + }, 6648 + { 6649 + "a": "JH", 6650 + "b": "P", 6651 + "id": "JH_P", 6652 + "weight": 0.5 6653 + }, 6654 + { 6655 + "a": "JH", 6656 + "b": "R", 6657 + "id": "JH_R", 6658 + "weight": 0.5 6659 + }, 6660 + { 6661 + "a": "JH", 6662 + "b": "S", 6663 + "id": "JH_S", 6664 + "weight": 0.5 6665 + }, 6666 + { 6667 + "a": "JH", 6668 + "b": "SH", 6669 + "id": "JH_SH", 6670 + "weight": 0.5 6671 + }, 6672 + { 6673 + "a": "JH", 6674 + "b": "T", 6675 + "id": "JH_T", 6676 + "weight": 0.5 6677 + }, 6678 + { 6679 + "a": "JH", 6680 + "b": "TH", 6681 + "id": "JH_TH", 6682 + "weight": 0.5 6683 + }, 6684 + { 6685 + "a": "JH", 6686 + "b": "UH", 6687 + "id": "JH_UH", 6688 + "weight": 0.5 6689 + }, 6690 + { 6691 + "a": "JH", 6692 + "b": "UW", 6693 + "id": "JH_UW", 6694 + "weight": 0.5 6695 + }, 6696 + { 6697 + "a": "JH", 6698 + "b": "V", 6699 + "id": "JH_V", 6700 + "weight": 0.5 6701 + }, 6702 + { 6703 + "a": "JH", 6704 + "b": "W", 6705 + "id": "JH_W", 6706 + "weight": 0.5 6707 + }, 6708 + { 6709 + "a": "JH", 6710 + "b": "Y", 6711 + "id": "JH_Y", 6712 + "weight": 0.5 6713 + }, 6714 + { 6715 + "a": "JH", 6716 + "b": "Z", 6717 + "id": "JH_Z", 6718 + "weight": 0.5 6719 + }, 6720 + { 6721 + "a": "K", 6722 + "b": "B", 6723 + "id": "K_B", 6724 + "weight": 0.5 6725 + }, 6726 + { 6727 + "a": "K", 6728 + "b": "CH", 6729 + "id": "K_CH", 6730 + "weight": 0.5 6731 + }, 6732 + { 6733 + "a": "K", 6734 + "b": "D", 6735 + "id": "K_D", 6736 + "weight": 0.5 6737 + }, 6738 + { 6739 + "a": "K", 6740 + "b": "DH", 6741 + "id": "K_DH", 6742 + "weight": 0.5 6743 + }, 6744 + { 6745 + "a": "K", 6746 + "b": "F", 6747 + "id": "K_F", 6748 + "weight": 0.5 6749 + }, 6750 + { 6751 + "a": "K", 6752 + "b": "G", 6753 + "id": "K_G", 6754 + "weight": 0.5 6755 + }, 6756 + { 6757 + "a": "K", 6758 + "b": "JH", 6759 + "id": "K_JH", 6760 + "weight": 0.5 6761 + }, 6762 + { 6763 + "a": "K", 6764 + "b": "K", 6765 + "id": "K_K", 6766 + "weight": 0.5 6767 + }, 6768 + { 6769 + "a": "K", 6770 + "b": "L", 6771 + "id": "K_L", 6772 + "weight": 0.5 6773 + }, 6774 + { 6775 + "a": "K", 6776 + "b": "M", 6777 + "id": "K_M", 6778 + "weight": 0.5 6779 + }, 6780 + { 6781 + "a": "K", 6782 + "b": "N", 6783 + "id": "K_N", 6784 + "weight": 0.5 6785 + }, 6786 + { 6787 + "a": "K", 6788 + "b": "NG", 6789 + "id": "K_NG", 6790 + "weight": 0.5 6791 + }, 6792 + { 6793 + "a": "K", 6794 + "b": "P", 6795 + "id": "K_P", 6796 + "weight": 0.5 6797 + }, 6798 + { 6799 + "a": "K", 6800 + "b": "R", 6801 + "id": "K_R", 6802 + "weight": 0.5 6803 + }, 6804 + { 6805 + "a": "K", 6806 + "b": "S", 6807 + "id": "K_S", 6808 + "weight": 0.5 6809 + }, 6810 + { 6811 + "a": "K", 6812 + "b": "SH", 6813 + "id": "K_SH", 6814 + "weight": 0.5 6815 + }, 6816 + { 6817 + "a": "K", 6818 + "b": "T", 6819 + "id": "K_T", 6820 + "weight": 0.5 6821 + }, 6822 + { 6823 + "a": "K", 6824 + "b": "TH", 6825 + "id": "K_TH", 6826 + "weight": 0.5 6827 + }, 6828 + { 6829 + "a": "K", 6830 + "b": "V", 6831 + "id": "K_V", 6832 + "weight": 0.5 6833 + }, 6834 + { 6835 + "a": "K", 6836 + "b": "W", 6837 + "id": "K_W", 6838 + "weight": 0.5 6839 + }, 6840 + { 6841 + "a": "K", 6842 + "b": "Y", 6843 + "id": "K_Y", 6844 + "weight": 0.5 6845 + }, 6846 + { 6847 + "a": "K", 6848 + "b": "Z", 6849 + "id": "K_Z", 6850 + "weight": 0.5 6851 + }, 6852 + { 6853 + "a": "L", 6854 + "b": "AA", 6855 + "id": "L_AA", 6856 + "weight": 0.5 6857 + }, 6858 + { 6859 + "a": "L", 6860 + "b": "AE", 6861 + "id": "L_AE", 6862 + "weight": 0.5 6863 + }, 6864 + { 6865 + "a": "L", 6866 + "b": "AH", 6867 + "id": "L_AH", 6868 + "weight": 0.5 6869 + }, 6870 + { 6871 + "a": "L", 6872 + "b": "AO", 6873 + "id": "L_AO", 6874 + "weight": 0.5 6875 + }, 6876 + { 6877 + "a": "L", 6878 + "b": "AW", 6879 + "id": "L_AW", 6880 + "weight": 0.5 6881 + }, 6882 + { 6883 + "a": "L", 6884 + "b": "AY", 6885 + "id": "L_AY", 6886 + "weight": 0.5 6887 + }, 6888 + { 6889 + "a": "L", 6890 + "b": "B", 6891 + "id": "L_B", 6892 + "weight": 0.5 6893 + }, 6894 + { 6895 + "a": "L", 6896 + "b": "CH", 6897 + "id": "L_CH", 6898 + "weight": 0.5 6899 + }, 6900 + { 6901 + "a": "L", 6902 + "b": "D", 6903 + "id": "L_D", 6904 + "weight": 0.5 6905 + }, 6906 + { 6907 + "a": "L", 6908 + "b": "DH", 6909 + "id": "L_DH", 6910 + "weight": 0.5 6911 + }, 6912 + { 6913 + "a": "L", 6914 + "b": "EH", 6915 + "id": "L_EH", 6916 + "weight": 0.5 6917 + }, 6918 + { 6919 + "a": "L", 6920 + "b": "ER", 6921 + "id": "L_ER", 6922 + "weight": 0.5 6923 + }, 6924 + { 6925 + "a": "L", 6926 + "b": "EY", 6927 + "id": "L_EY", 6928 + "weight": 0.5 6929 + }, 6930 + { 6931 + "a": "L", 6932 + "b": "F", 6933 + "id": "L_F", 6934 + "weight": 0.5 6935 + }, 6936 + { 6937 + "a": "L", 6938 + "b": "G", 6939 + "id": "L_G", 6940 + "weight": 0.5 6941 + }, 6942 + { 6943 + "a": "L", 6944 + "b": "IH", 6945 + "id": "L_IH", 6946 + "weight": 0.5 6947 + }, 6948 + { 6949 + "a": "L", 6950 + "b": "IY", 6951 + "id": "L_IY", 6952 + "weight": 0.5 6953 + }, 6954 + { 6955 + "a": "L", 6956 + "b": "JH", 6957 + "id": "L_JH", 6958 + "weight": 0.5 6959 + }, 6960 + { 6961 + "a": "L", 6962 + "b": "K", 6963 + "id": "L_K", 6964 + "weight": 0.5 6965 + }, 6966 + { 6967 + "a": "L", 6968 + "b": "L", 6969 + "id": "L_L", 6970 + "weight": 0.5 6971 + }, 6972 + { 6973 + "a": "L", 6974 + "b": "M", 6975 + "id": "L_M", 6976 + "weight": 0.5 6977 + }, 6978 + { 6979 + "a": "L", 6980 + "b": "N", 6981 + "id": "L_N", 6982 + "weight": 0.5 6983 + }, 6984 + { 6985 + "a": "L", 6986 + "b": "NG", 6987 + "id": "L_NG", 6988 + "weight": 0.5 6989 + }, 6990 + { 6991 + "a": "L", 6992 + "b": "OW", 6993 + "id": "L_OW", 6994 + "weight": 0.5 6995 + }, 6996 + { 6997 + "a": "L", 6998 + "b": "OY", 6999 + "id": "L_OY", 7000 + "weight": 0.5 7001 + }, 7002 + { 7003 + "a": "L", 7004 + "b": "P", 7005 + "id": "L_P", 7006 + "weight": 0.5 7007 + }, 7008 + { 7009 + "a": "L", 7010 + "b": "R", 7011 + "id": "L_R", 7012 + "weight": 0.5 7013 + }, 7014 + { 7015 + "a": "L", 7016 + "b": "S", 7017 + "id": "L_S", 7018 + "weight": 0.5 7019 + }, 7020 + { 7021 + "a": "L", 7022 + "b": "SH", 7023 + "id": "L_SH", 7024 + "weight": 0.5 7025 + }, 7026 + { 7027 + "a": "L", 7028 + "b": "T", 7029 + "id": "L_T", 7030 + "weight": 0.5 7031 + }, 7032 + { 7033 + "a": "L", 7034 + "b": "TH", 7035 + "id": "L_TH", 7036 + "weight": 0.5 7037 + }, 7038 + { 7039 + "a": "L", 7040 + "b": "UH", 7041 + "id": "L_UH", 7042 + "weight": 0.5 7043 + }, 7044 + { 7045 + "a": "L", 7046 + "b": "UW", 7047 + "id": "L_UW", 7048 + "weight": 0.5 7049 + }, 7050 + { 7051 + "a": "L", 7052 + "b": "V", 7053 + "id": "L_V", 7054 + "weight": 0.5 7055 + }, 7056 + { 7057 + "a": "L", 7058 + "b": "W", 7059 + "id": "L_W", 7060 + "weight": 0.5 7061 + }, 7062 + { 7063 + "a": "L", 7064 + "b": "Y", 7065 + "id": "L_Y", 7066 + "weight": 0.5 7067 + }, 7068 + { 7069 + "a": "L", 7070 + "b": "Z", 7071 + "id": "L_Z", 7072 + "weight": 0.5 7073 + }, 7074 + { 7075 + "a": "L", 7076 + "b": "ZH", 7077 + "id": "L_ZH", 7078 + "weight": 0.5 7079 + }, 7080 + { 7081 + "a": "M", 7082 + "b": "B", 7083 + "id": "M_B", 7084 + "weight": 0.5 7085 + }, 7086 + { 7087 + "a": "M", 7088 + "b": "CH", 7089 + "id": "M_CH", 7090 + "weight": 0.5 7091 + }, 7092 + { 7093 + "a": "M", 7094 + "b": "D", 7095 + "id": "M_D", 7096 + "weight": 0.5 7097 + }, 7098 + { 7099 + "a": "M", 7100 + "b": "DH", 7101 + "id": "M_DH", 7102 + "weight": 0.5 7103 + }, 7104 + { 7105 + "a": "M", 7106 + "b": "F", 7107 + "id": "M_F", 7108 + "weight": 0.5 7109 + }, 7110 + { 7111 + "a": "M", 7112 + "b": "G", 7113 + "id": "M_G", 7114 + "weight": 0.5 7115 + }, 7116 + { 7117 + "a": "M", 7118 + "b": "JH", 7119 + "id": "M_JH", 7120 + "weight": 0.5 7121 + }, 7122 + { 7123 + "a": "M", 7124 + "b": "K", 7125 + "id": "M_K", 7126 + "weight": 0.5 7127 + }, 7128 + { 7129 + "a": "M", 7130 + "b": "L", 7131 + "id": "M_L", 7132 + "weight": 0.5 7133 + }, 7134 + { 7135 + "a": "M", 7136 + "b": "M", 7137 + "id": "M_M", 7138 + "weight": 0.5 7139 + }, 7140 + { 7141 + "a": "M", 7142 + "b": "N", 7143 + "id": "M_N", 7144 + "weight": 0.5 7145 + }, 7146 + { 7147 + "a": "M", 7148 + "b": "NG", 7149 + "id": "M_NG", 7150 + "weight": 0.5 7151 + }, 7152 + { 7153 + "a": "M", 7154 + "b": "P", 7155 + "id": "M_P", 7156 + "weight": 0.5 7157 + }, 7158 + { 7159 + "a": "M", 7160 + "b": "R", 7161 + "id": "M_R", 7162 + "weight": 0.5 7163 + }, 7164 + { 7165 + "a": "M", 7166 + "b": "S", 7167 + "id": "M_S", 7168 + "weight": 0.5 7169 + }, 7170 + { 7171 + "a": "M", 7172 + "b": "SH", 7173 + "id": "M_SH", 7174 + "weight": 0.5 7175 + }, 7176 + { 7177 + "a": "M", 7178 + "b": "T", 7179 + "id": "M_T", 7180 + "weight": 0.5 7181 + }, 7182 + { 7183 + "a": "M", 7184 + "b": "TH", 7185 + "id": "M_TH", 7186 + "weight": 0.5 7187 + }, 7188 + { 7189 + "a": "M", 7190 + "b": "V", 7191 + "id": "M_V", 7192 + "weight": 0.5 7193 + }, 7194 + { 7195 + "a": "M", 7196 + "b": "W", 7197 + "id": "M_W", 7198 + "weight": 0.5 7199 + }, 7200 + { 7201 + "a": "M", 7202 + "b": "Y", 7203 + "id": "M_Y", 7204 + "weight": 0.5 7205 + }, 7206 + { 7207 + "a": "M", 7208 + "b": "Z", 7209 + "id": "M_Z", 7210 + "weight": 0.5 7211 + }, 7212 + { 7213 + "a": "N", 7214 + "b": "B", 7215 + "id": "N_B", 7216 + "weight": 0.5 7217 + }, 7218 + { 7219 + "a": "N", 7220 + "b": "CH", 7221 + "id": "N_CH", 7222 + "weight": 0.5 7223 + }, 7224 + { 7225 + "a": "N", 7226 + "b": "D", 7227 + "id": "N_D", 7228 + "weight": 0.5 7229 + }, 7230 + { 7231 + "a": "N", 7232 + "b": "DH", 7233 + "id": "N_DH", 7234 + "weight": 0.5 7235 + }, 7236 + { 7237 + "a": "N", 7238 + "b": "F", 7239 + "id": "N_F", 7240 + "weight": 0.5 7241 + }, 7242 + { 7243 + "a": "N", 7244 + "b": "G", 7245 + "id": "N_G", 7246 + "weight": 0.5 7247 + }, 7248 + { 7249 + "a": "N", 7250 + "b": "JH", 7251 + "id": "N_JH", 7252 + "weight": 0.5 7253 + }, 7254 + { 7255 + "a": "N", 7256 + "b": "K", 7257 + "id": "N_K", 7258 + "weight": 0.5 7259 + }, 7260 + { 7261 + "a": "N", 7262 + "b": "L", 7263 + "id": "N_L", 7264 + "weight": 0.5 7265 + }, 7266 + { 7267 + "a": "N", 7268 + "b": "M", 7269 + "id": "N_M", 7270 + "weight": 0.5 7271 + }, 7272 + { 7273 + "a": "N", 7274 + "b": "N", 7275 + "id": "N_N", 7276 + "weight": 0.5 7277 + }, 7278 + { 7279 + "a": "N", 7280 + "b": "NG", 7281 + "id": "N_NG", 7282 + "weight": 0.5 7283 + }, 7284 + { 7285 + "a": "N", 7286 + "b": "P", 7287 + "id": "N_P", 7288 + "weight": 0.5 7289 + }, 7290 + { 7291 + "a": "N", 7292 + "b": "R", 7293 + "id": "N_R", 7294 + "weight": 0.5 7295 + }, 7296 + { 7297 + "a": "N", 7298 + "b": "S", 7299 + "id": "N_S", 7300 + "weight": 0.5 7301 + }, 7302 + { 7303 + "a": "N", 7304 + "b": "SH", 7305 + "id": "N_SH", 7306 + "weight": 0.5 7307 + }, 7308 + { 7309 + "a": "N", 7310 + "b": "T", 7311 + "id": "N_T", 7312 + "weight": 0.5 7313 + }, 7314 + { 7315 + "a": "N", 7316 + "b": "TH", 7317 + "id": "N_TH", 7318 + "weight": 0.5 7319 + }, 7320 + { 7321 + "a": "N", 7322 + "b": "V", 7323 + "id": "N_V", 7324 + "weight": 0.5 7325 + }, 7326 + { 7327 + "a": "N", 7328 + "b": "W", 7329 + "id": "N_W", 7330 + "weight": 0.5 7331 + }, 7332 + { 7333 + "a": "N", 7334 + "b": "Y", 7335 + "id": "N_Y", 7336 + "weight": 0.5 7337 + }, 7338 + { 7339 + "a": "N", 7340 + "b": "Z", 7341 + "id": "N_Z", 7342 + "weight": 0.5 7343 + }, 7344 + { 7345 + "a": "NG", 7346 + "b": "G", 7347 + "id": "NG_G", 7348 + "weight": 0.5 7349 + }, 7350 + { 7351 + "a": "NG", 7352 + "b": "K", 7353 + "id": "NG_K", 7354 + "weight": 0.5 7355 + }, 7356 + { 7357 + "a": "OW", 7358 + "b": "CH", 7359 + "id": "OW_CH", 7360 + "weight": 0.5 7361 + }, 7362 + { 7363 + "a": "OW", 7364 + "b": "JH", 7365 + "id": "OW_JH", 7366 + "weight": 0.5 7367 + }, 7368 + { 7369 + "a": "OW", 7370 + "b": "L", 7371 + "id": "OW_L", 7372 + "weight": 0.5 7373 + }, 7374 + { 7375 + "a": "OW", 7376 + "b": "R", 7377 + "id": "OW_R", 7378 + "weight": 0.5 7379 + }, 7380 + { 7381 + "a": "OW", 7382 + "b": "W", 7383 + "id": "OW_W", 7384 + "weight": 0.5 7385 + }, 7386 + { 7387 + "a": "OW", 7388 + "b": "Y", 7389 + "id": "OW_Y", 7390 + "weight": 0.5 7391 + }, 7392 + { 7393 + "a": "OY", 7394 + "b": "CH", 7395 + "id": "OY_CH", 7396 + "weight": 0.5 7397 + }, 7398 + { 7399 + "a": "OY", 7400 + "b": "JH", 7401 + "id": "OY_JH", 7402 + "weight": 0.5 7403 + }, 7404 + { 7405 + "a": "OY", 7406 + "b": "L", 7407 + "id": "OY_L", 7408 + "weight": 0.5 7409 + }, 7410 + { 7411 + "a": "OY", 7412 + "b": "R", 7413 + "id": "OY_R", 7414 + "weight": 0.5 7415 + }, 7416 + { 7417 + "a": "OY", 7418 + "b": "W", 7419 + "id": "OY_W", 7420 + "weight": 0.5 7421 + }, 7422 + { 7423 + "a": "OY", 7424 + "b": "Y", 7425 + "id": "OY_Y", 7426 + "weight": 0.5 7427 + }, 7428 + { 7429 + "a": "P", 7430 + "b": "B", 7431 + "id": "P_B", 7432 + "weight": 0.5 7433 + }, 7434 + { 7435 + "a": "P", 7436 + "b": "CH", 7437 + "id": "P_CH", 7438 + "weight": 0.5 7439 + }, 7440 + { 7441 + "a": "P", 7442 + "b": "D", 7443 + "id": "P_D", 7444 + "weight": 0.5 7445 + }, 7446 + { 7447 + "a": "P", 7448 + "b": "DH", 7449 + "id": "P_DH", 7450 + "weight": 0.5 7451 + }, 7452 + { 7453 + "a": "P", 7454 + "b": "F", 7455 + "id": "P_F", 7456 + "weight": 0.5 7457 + }, 7458 + { 7459 + "a": "P", 7460 + "b": "G", 7461 + "id": "P_G", 7462 + "weight": 0.5 7463 + }, 7464 + { 7465 + "a": "P", 7466 + "b": "JH", 7467 + "id": "P_JH", 7468 + "weight": 0.5 7469 + }, 7470 + { 7471 + "a": "P", 7472 + "b": "K", 7473 + "id": "P_K", 7474 + "weight": 0.5 7475 + }, 7476 + { 7477 + "a": "P", 7478 + "b": "L", 7479 + "id": "P_L", 7480 + "weight": 0.5 7481 + }, 7482 + { 7483 + "a": "P", 7484 + "b": "M", 7485 + "id": "P_M", 7486 + "weight": 0.5 7487 + }, 7488 + { 7489 + "a": "P", 7490 + "b": "N", 7491 + "id": "P_N", 7492 + "weight": 0.5 7493 + }, 7494 + { 7495 + "a": "P", 7496 + "b": "NG", 7497 + "id": "P_NG", 7498 + "weight": 0.5 7499 + }, 7500 + { 7501 + "a": "P", 7502 + "b": "P", 7503 + "id": "P_P", 7504 + "weight": 0.5 7505 + }, 7506 + { 7507 + "a": "P", 7508 + "b": "R", 7509 + "id": "P_R", 7510 + "weight": 0.5 7511 + }, 7512 + { 7513 + "a": "P", 7514 + "b": "S", 7515 + "id": "P_S", 7516 + "weight": 0.5 7517 + }, 7518 + { 7519 + "a": "P", 7520 + "b": "SH", 7521 + "id": "P_SH", 7522 + "weight": 0.5 7523 + }, 7524 + { 7525 + "a": "P", 7526 + "b": "T", 7527 + "id": "P_T", 7528 + "weight": 0.5 7529 + }, 7530 + { 7531 + "a": "P", 7532 + "b": "TH", 7533 + "id": "P_TH", 7534 + "weight": 0.5 7535 + }, 7536 + { 7537 + "a": "P", 7538 + "b": "V", 7539 + "id": "P_V", 7540 + "weight": 0.5 7541 + }, 7542 + { 7543 + "a": "P", 7544 + "b": "W", 7545 + "id": "P_W", 7546 + "weight": 0.5 7547 + }, 7548 + { 7549 + "a": "P", 7550 + "b": "Y", 7551 + "id": "P_Y", 7552 + "weight": 0.5 7553 + }, 7554 + { 7555 + "a": "P", 7556 + "b": "Z", 7557 + "id": "P_Z", 7558 + "weight": 0.5 7559 + }, 7560 + { 7561 + "a": "R", 7562 + "b": "AA", 7563 + "id": "R_AA", 7564 + "weight": 0.5 7565 + }, 7566 + { 7567 + "a": "R", 7568 + "b": "AE", 7569 + "id": "R_AE", 7570 + "weight": 0.5 7571 + }, 7572 + { 7573 + "a": "R", 7574 + "b": "AH", 7575 + "id": "R_AH", 7576 + "weight": 0.5 7577 + }, 7578 + { 7579 + "a": "R", 7580 + "b": "AO", 7581 + "id": "R_AO", 7582 + "weight": 0.5 7583 + }, 7584 + { 7585 + "a": "R", 7586 + "b": "AW", 7587 + "id": "R_AW", 7588 + "weight": 0.5 7589 + }, 7590 + { 7591 + "a": "R", 7592 + "b": "AY", 7593 + "id": "R_AY", 7594 + "weight": 0.5 7595 + }, 7596 + { 7597 + "a": "R", 7598 + "b": "B", 7599 + "id": "R_B", 7600 + "weight": 0.5 7601 + }, 7602 + { 7603 + "a": "R", 7604 + "b": "CH", 7605 + "id": "R_CH", 7606 + "weight": 0.5 7607 + }, 7608 + { 7609 + "a": "R", 7610 + "b": "D", 7611 + "id": "R_D", 7612 + "weight": 0.5 7613 + }, 7614 + { 7615 + "a": "R", 7616 + "b": "DH", 7617 + "id": "R_DH", 7618 + "weight": 0.5 7619 + }, 7620 + { 7621 + "a": "R", 7622 + "b": "EH", 7623 + "id": "R_EH", 7624 + "weight": 0.5 7625 + }, 7626 + { 7627 + "a": "R", 7628 + "b": "ER", 7629 + "id": "R_ER", 7630 + "weight": 0.5 7631 + }, 7632 + { 7633 + "a": "R", 7634 + "b": "EY", 7635 + "id": "R_EY", 7636 + "weight": 0.5 7637 + }, 7638 + { 7639 + "a": "R", 7640 + "b": "F", 7641 + "id": "R_F", 7642 + "weight": 0.5 7643 + }, 7644 + { 7645 + "a": "R", 7646 + "b": "G", 7647 + "id": "R_G", 7648 + "weight": 0.5 7649 + }, 7650 + { 7651 + "a": "R", 7652 + "b": "IH", 7653 + "id": "R_IH", 7654 + "weight": 0.5 7655 + }, 7656 + { 7657 + "a": "R", 7658 + "b": "IY", 7659 + "id": "R_IY", 7660 + "weight": 0.5 7661 + }, 7662 + { 7663 + "a": "R", 7664 + "b": "JH", 7665 + "id": "R_JH", 7666 + "weight": 0.5 7667 + }, 7668 + { 7669 + "a": "R", 7670 + "b": "K", 7671 + "id": "R_K", 7672 + "weight": 0.5 7673 + }, 7674 + { 7675 + "a": "R", 7676 + "b": "L", 7677 + "id": "R_L", 7678 + "weight": 0.5 7679 + }, 7680 + { 7681 + "a": "R", 7682 + "b": "M", 7683 + "id": "R_M", 7684 + "weight": 0.5 7685 + }, 7686 + { 7687 + "a": "R", 7688 + "b": "N", 7689 + "id": "R_N", 7690 + "weight": 0.5 7691 + }, 7692 + { 7693 + "a": "R", 7694 + "b": "NG", 7695 + "id": "R_NG", 7696 + "weight": 0.5 7697 + }, 7698 + { 7699 + "a": "R", 7700 + "b": "OW", 7701 + "id": "R_OW", 7702 + "weight": 0.5 7703 + }, 7704 + { 7705 + "a": "R", 7706 + "b": "OY", 7707 + "id": "R_OY", 7708 + "weight": 0.5 7709 + }, 7710 + { 7711 + "a": "R", 7712 + "b": "P", 7713 + "id": "R_P", 7714 + "weight": 0.5 7715 + }, 7716 + { 7717 + "a": "R", 7718 + "b": "R", 7719 + "id": "R_R", 7720 + "weight": 0.5 7721 + }, 7722 + { 7723 + "a": "R", 7724 + "b": "S", 7725 + "id": "R_S", 7726 + "weight": 0.5 7727 + }, 7728 + { 7729 + "a": "R", 7730 + "b": "SH", 7731 + "id": "R_SH", 7732 + "weight": 0.5 7733 + }, 7734 + { 7735 + "a": "R", 7736 + "b": "T", 7737 + "id": "R_T", 7738 + "weight": 0.5 7739 + }, 7740 + { 7741 + "a": "R", 7742 + "b": "TH", 7743 + "id": "R_TH", 7744 + "weight": 0.5 7745 + }, 7746 + { 7747 + "a": "R", 7748 + "b": "UH", 7749 + "id": "R_UH", 7750 + "weight": 0.5 7751 + }, 7752 + { 7753 + "a": "R", 7754 + "b": "UW", 7755 + "id": "R_UW", 7756 + "weight": 0.5 7757 + }, 7758 + { 7759 + "a": "R", 7760 + "b": "V", 7761 + "id": "R_V", 7762 + "weight": 0.5 7763 + }, 7764 + { 7765 + "a": "R", 7766 + "b": "W", 7767 + "id": "R_W", 7768 + "weight": 0.5 7769 + }, 7770 + { 7771 + "a": "R", 7772 + "b": "Y", 7773 + "id": "R_Y", 7774 + "weight": 0.5 7775 + }, 7776 + { 7777 + "a": "R", 7778 + "b": "Z", 7779 + "id": "R_Z", 7780 + "weight": 0.5 7781 + }, 7782 + { 7783 + "a": "S", 7784 + "b": "B", 7785 + "id": "S_B", 7786 + "weight": 0.5 7787 + }, 7788 + { 7789 + "a": "S", 7790 + "b": "CH", 7791 + "id": "S_CH", 7792 + "weight": 0.5 7793 + }, 7794 + { 7795 + "a": "S", 7796 + "b": "D", 7797 + "id": "S_D", 7798 + "weight": 0.5 7799 + }, 7800 + { 7801 + "a": "S", 7802 + "b": "DH", 7803 + "id": "S_DH", 7804 + "weight": 0.5 7805 + }, 7806 + { 7807 + "a": "S", 7808 + "b": "F", 7809 + "id": "S_F", 7810 + "weight": 0.5 7811 + }, 7812 + { 7813 + "a": "S", 7814 + "b": "G", 7815 + "id": "S_G", 7816 + "weight": 0.5 7817 + }, 7818 + { 7819 + "a": "S", 7820 + "b": "JH", 7821 + "id": "S_JH", 7822 + "weight": 0.5 7823 + }, 7824 + { 7825 + "a": "S", 7826 + "b": "K", 7827 + "id": "S_K", 7828 + "weight": 0.5 7829 + }, 7830 + { 7831 + "a": "S", 7832 + "b": "L", 7833 + "id": "S_L", 7834 + "weight": 0.5 7835 + }, 7836 + { 7837 + "a": "S", 7838 + "b": "M", 7839 + "id": "S_M", 7840 + "weight": 0.5 7841 + }, 7842 + { 7843 + "a": "S", 7844 + "b": "N", 7845 + "id": "S_N", 7846 + "weight": 0.5 7847 + }, 7848 + { 7849 + "a": "S", 7850 + "b": "NG", 7851 + "id": "S_NG", 7852 + "weight": 0.5 7853 + }, 7854 + { 7855 + "a": "S", 7856 + "b": "P", 7857 + "id": "S_P", 7858 + "weight": 0.5 7859 + }, 7860 + { 7861 + "a": "S", 7862 + "b": "R", 7863 + "id": "S_R", 7864 + "weight": 0.5 7865 + }, 7866 + { 7867 + "a": "S", 7868 + "b": "S", 7869 + "id": "S_S", 7870 + "weight": 0.5 7871 + }, 7872 + { 7873 + "a": "S", 7874 + "b": "SH", 7875 + "id": "S_SH", 7876 + "weight": 0.5 7877 + }, 7878 + { 7879 + "a": "S", 7880 + "b": "T", 7881 + "id": "S_T", 7882 + "weight": 0.5 7883 + }, 7884 + { 7885 + "a": "S", 7886 + "b": "TH", 7887 + "id": "S_TH", 7888 + "weight": 0.5 7889 + }, 7890 + { 7891 + "a": "S", 7892 + "b": "V", 7893 + "id": "S_V", 7894 + "weight": 0.5 7895 + }, 7896 + { 7897 + "a": "S", 7898 + "b": "W", 7899 + "id": "S_W", 7900 + "weight": 0.5 7901 + }, 7902 + { 7903 + "a": "S", 7904 + "b": "Y", 7905 + "id": "S_Y", 7906 + "weight": 0.5 7907 + }, 7908 + { 7909 + "a": "S", 7910 + "b": "Z", 7911 + "id": "S_Z", 7912 + "weight": 0.5 7913 + }, 7914 + { 7915 + "a": "SH", 7916 + "b": "B", 7917 + "id": "SH_B", 7918 + "weight": 0.5 7919 + }, 7920 + { 7921 + "a": "SH", 7922 + "b": "CH", 7923 + "id": "SH_CH", 7924 + "weight": 0.5 7925 + }, 7926 + { 7927 + "a": "SH", 7928 + "b": "D", 7929 + "id": "SH_D", 7930 + "weight": 0.5 7931 + }, 7932 + { 7933 + "a": "SH", 7934 + "b": "DH", 7935 + "id": "SH_DH", 7936 + "weight": 0.5 7937 + }, 7938 + { 7939 + "a": "SH", 7940 + "b": "F", 7941 + "id": "SH_F", 7942 + "weight": 0.5 7943 + }, 7944 + { 7945 + "a": "SH", 7946 + "b": "G", 7947 + "id": "SH_G", 7948 + "weight": 0.5 7949 + }, 7950 + { 7951 + "a": "SH", 7952 + "b": "JH", 7953 + "id": "SH_JH", 7954 + "weight": 0.5 7955 + }, 7956 + { 7957 + "a": "SH", 7958 + "b": "K", 7959 + "id": "SH_K", 7960 + "weight": 0.5 7961 + }, 7962 + { 7963 + "a": "SH", 7964 + "b": "L", 7965 + "id": "SH_L", 7966 + "weight": 0.5 7967 + }, 7968 + { 7969 + "a": "SH", 7970 + "b": "M", 7971 + "id": "SH_M", 7972 + "weight": 0.5 7973 + }, 7974 + { 7975 + "a": "SH", 7976 + "b": "N", 7977 + "id": "SH_N", 7978 + "weight": 0.5 7979 + }, 7980 + { 7981 + "a": "SH", 7982 + "b": "NG", 7983 + "id": "SH_NG", 7984 + "weight": 0.5 7985 + }, 7986 + { 7987 + "a": "SH", 7988 + "b": "P", 7989 + "id": "SH_P", 7990 + "weight": 0.5 7991 + }, 7992 + { 7993 + "a": "SH", 7994 + "b": "R", 7995 + "id": "SH_R", 7996 + "weight": 0.5 7997 + }, 7998 + { 7999 + "a": "SH", 8000 + "b": "S", 8001 + "id": "SH_S", 8002 + "weight": 0.5 8003 + }, 8004 + { 8005 + "a": "SH", 8006 + "b": "SH", 8007 + "id": "SH_SH", 8008 + "weight": 0.5 8009 + }, 8010 + { 8011 + "a": "SH", 8012 + "b": "T", 8013 + "id": "SH_T", 8014 + "weight": 0.5 8015 + }, 8016 + { 8017 + "a": "SH", 8018 + "b": "TH", 8019 + "id": "SH_TH", 8020 + "weight": 0.5 8021 + }, 8022 + { 8023 + "a": "SH", 8024 + "b": "V", 8025 + "id": "SH_V", 8026 + "weight": 0.5 8027 + }, 8028 + { 8029 + "a": "SH", 8030 + "b": "W", 8031 + "id": "SH_W", 8032 + "weight": 0.5 8033 + }, 8034 + { 8035 + "a": "SH", 8036 + "b": "Y", 8037 + "id": "SH_Y", 8038 + "weight": 0.5 8039 + }, 8040 + { 8041 + "a": "SH", 8042 + "b": "Z", 8043 + "id": "SH_Z", 8044 + "weight": 0.5 8045 + }, 8046 + { 8047 + "a": "T", 8048 + "b": "B", 8049 + "id": "T_B", 8050 + "weight": 0.5 8051 + }, 8052 + { 8053 + "a": "T", 8054 + "b": "CH", 8055 + "id": "T_CH", 8056 + "weight": 0.5 8057 + }, 8058 + { 8059 + "a": "T", 8060 + "b": "D", 8061 + "id": "T_D", 8062 + "weight": 0.5 8063 + }, 8064 + { 8065 + "a": "T", 8066 + "b": "DH", 8067 + "id": "T_DH", 8068 + "weight": 0.5 8069 + }, 8070 + { 8071 + "a": "T", 8072 + "b": "F", 8073 + "id": "T_F", 8074 + "weight": 0.5 8075 + }, 8076 + { 8077 + "a": "T", 8078 + "b": "G", 8079 + "id": "T_G", 8080 + "weight": 0.5 8081 + }, 8082 + { 8083 + "a": "T", 8084 + "b": "JH", 8085 + "id": "T_JH", 8086 + "weight": 0.5 8087 + }, 8088 + { 8089 + "a": "T", 8090 + "b": "K", 8091 + "id": "T_K", 8092 + "weight": 0.5 8093 + }, 8094 + { 8095 + "a": "T", 8096 + "b": "L", 8097 + "id": "T_L", 8098 + "weight": 0.5 8099 + }, 8100 + { 8101 + "a": "T", 8102 + "b": "M", 8103 + "id": "T_M", 8104 + "weight": 0.5 8105 + }, 8106 + { 8107 + "a": "T", 8108 + "b": "N", 8109 + "id": "T_N", 8110 + "weight": 0.5 8111 + }, 8112 + { 8113 + "a": "T", 8114 + "b": "NG", 8115 + "id": "T_NG", 8116 + "weight": 0.5 8117 + }, 8118 + { 8119 + "a": "T", 8120 + "b": "P", 8121 + "id": "T_P", 8122 + "weight": 0.5 8123 + }, 8124 + { 8125 + "a": "T", 8126 + "b": "R", 8127 + "id": "T_R", 8128 + "weight": 0.5 8129 + }, 8130 + { 8131 + "a": "T", 8132 + "b": "S", 8133 + "id": "T_S", 8134 + "weight": 0.5 8135 + }, 8136 + { 8137 + "a": "T", 8138 + "b": "SH", 8139 + "id": "T_SH", 8140 + "weight": 0.5 8141 + }, 8142 + { 8143 + "a": "T", 8144 + "b": "T", 8145 + "id": "T_T", 8146 + "weight": 0.5 8147 + }, 8148 + { 8149 + "a": "T", 8150 + "b": "TH", 8151 + "id": "T_TH", 8152 + "weight": 0.5 8153 + }, 8154 + { 8155 + "a": "T", 8156 + "b": "V", 8157 + "id": "T_V", 8158 + "weight": 0.5 8159 + }, 8160 + { 8161 + "a": "T", 8162 + "b": "W", 8163 + "id": "T_W", 8164 + "weight": 0.5 8165 + }, 8166 + { 8167 + "a": "T", 8168 + "b": "Y", 8169 + "id": "T_Y", 8170 + "weight": 0.5 8171 + }, 8172 + { 8173 + "a": "T", 8174 + "b": "Z", 8175 + "id": "T_Z", 8176 + "weight": 0.5 8177 + }, 8178 + { 8179 + "a": "TH", 8180 + "b": "B", 8181 + "id": "TH_B", 8182 + "weight": 0.5 8183 + }, 8184 + { 8185 + "a": "TH", 8186 + "b": "CH", 8187 + "id": "TH_CH", 8188 + "weight": 0.5 8189 + }, 8190 + { 8191 + "a": "TH", 8192 + "b": "D", 8193 + "id": "TH_D", 8194 + "weight": 0.5 8195 + }, 8196 + { 8197 + "a": "TH", 8198 + "b": "DH", 8199 + "id": "TH_DH", 8200 + "weight": 0.5 8201 + }, 8202 + { 8203 + "a": "TH", 8204 + "b": "F", 8205 + "id": "TH_F", 8206 + "weight": 0.5 8207 + }, 8208 + { 8209 + "a": "TH", 8210 + "b": "G", 8211 + "id": "TH_G", 8212 + "weight": 0.5 8213 + }, 8214 + { 8215 + "a": "TH", 8216 + "b": "JH", 8217 + "id": "TH_JH", 8218 + "weight": 0.5 8219 + }, 8220 + { 8221 + "a": "TH", 8222 + "b": "K", 8223 + "id": "TH_K", 8224 + "weight": 0.5 8225 + }, 8226 + { 8227 + "a": "TH", 8228 + "b": "L", 8229 + "id": "TH_L", 8230 + "weight": 0.5 8231 + }, 8232 + { 8233 + "a": "TH", 8234 + "b": "M", 8235 + "id": "TH_M", 8236 + "weight": 0.5 8237 + }, 8238 + { 8239 + "a": "TH", 8240 + "b": "N", 8241 + "id": "TH_N", 8242 + "weight": 0.5 8243 + }, 8244 + { 8245 + "a": "TH", 8246 + "b": "NG", 8247 + "id": "TH_NG", 8248 + "weight": 0.5 8249 + }, 8250 + { 8251 + "a": "TH", 8252 + "b": "P", 8253 + "id": "TH_P", 8254 + "weight": 0.5 8255 + }, 8256 + { 8257 + "a": "TH", 8258 + "b": "R", 8259 + "id": "TH_R", 8260 + "weight": 0.5 8261 + }, 8262 + { 8263 + "a": "TH", 8264 + "b": "S", 8265 + "id": "TH_S", 8266 + "weight": 0.5 8267 + }, 8268 + { 8269 + "a": "TH", 8270 + "b": "SH", 8271 + "id": "TH_SH", 8272 + "weight": 0.5 8273 + }, 8274 + { 8275 + "a": "TH", 8276 + "b": "T", 8277 + "id": "TH_T", 8278 + "weight": 0.5 8279 + }, 8280 + { 8281 + "a": "TH", 8282 + "b": "TH", 8283 + "id": "TH_TH", 8284 + "weight": 0.5 8285 + }, 8286 + { 8287 + "a": "TH", 8288 + "b": "V", 8289 + "id": "TH_V", 8290 + "weight": 0.5 8291 + }, 8292 + { 8293 + "a": "TH", 8294 + "b": "W", 8295 + "id": "TH_W", 8296 + "weight": 0.5 8297 + }, 8298 + { 8299 + "a": "TH", 8300 + "b": "Y", 8301 + "id": "TH_Y", 8302 + "weight": 0.5 8303 + }, 8304 + { 8305 + "a": "TH", 8306 + "b": "Z", 8307 + "id": "TH_Z", 8308 + "weight": 0.5 8309 + }, 8310 + { 8311 + "a": "UH", 8312 + "b": "CH", 8313 + "id": "UH_CH", 8314 + "weight": 0.5 8315 + }, 8316 + { 8317 + "a": "UH", 8318 + "b": "JH", 8319 + "id": "UH_JH", 8320 + "weight": 0.5 8321 + }, 8322 + { 8323 + "a": "UH", 8324 + "b": "L", 8325 + "id": "UH_L", 8326 + "weight": 0.5 8327 + }, 8328 + { 8329 + "a": "UH", 8330 + "b": "R", 8331 + "id": "UH_R", 8332 + "weight": 0.5 8333 + }, 8334 + { 8335 + "a": "UH", 8336 + "b": "W", 8337 + "id": "UH_W", 8338 + "weight": 0.5 8339 + }, 8340 + { 8341 + "a": "UH", 8342 + "b": "Y", 8343 + "id": "UH_Y", 8344 + "weight": 0.5 8345 + }, 8346 + { 8347 + "a": "UW", 8348 + "b": "CH", 8349 + "id": "UW_CH", 8350 + "weight": 0.5 8351 + }, 8352 + { 8353 + "a": "UW", 8354 + "b": "JH", 8355 + "id": "UW_JH", 8356 + "weight": 0.5 8357 + }, 8358 + { 8359 + "a": "UW", 8360 + "b": "L", 8361 + "id": "UW_L", 8362 + "weight": 0.5 8363 + }, 8364 + { 8365 + "a": "UW", 8366 + "b": "R", 8367 + "id": "UW_R", 8368 + "weight": 0.5 8369 + }, 8370 + { 8371 + "a": "UW", 8372 + "b": "W", 8373 + "id": "UW_W", 8374 + "weight": 0.5 8375 + }, 8376 + { 8377 + "a": "UW", 8378 + "b": "Y", 8379 + "id": "UW_Y", 8380 + "weight": 0.5 8381 + }, 8382 + { 8383 + "a": "V", 8384 + "b": "B", 8385 + "id": "V_B", 8386 + "weight": 0.5 8387 + }, 8388 + { 8389 + "a": "V", 8390 + "b": "CH", 8391 + "id": "V_CH", 8392 + "weight": 0.5 8393 + }, 8394 + { 8395 + "a": "V", 8396 + "b": "D", 8397 + "id": "V_D", 8398 + "weight": 0.5 8399 + }, 8400 + { 8401 + "a": "V", 8402 + "b": "DH", 8403 + "id": "V_DH", 8404 + "weight": 0.5 8405 + }, 8406 + { 8407 + "a": "V", 8408 + "b": "F", 8409 + "id": "V_F", 8410 + "weight": 0.5 8411 + }, 8412 + { 8413 + "a": "V", 8414 + "b": "G", 8415 + "id": "V_G", 8416 + "weight": 0.5 8417 + }, 8418 + { 8419 + "a": "V", 8420 + "b": "JH", 8421 + "id": "V_JH", 8422 + "weight": 0.5 8423 + }, 8424 + { 8425 + "a": "V", 8426 + "b": "K", 8427 + "id": "V_K", 8428 + "weight": 0.5 8429 + }, 8430 + { 8431 + "a": "V", 8432 + "b": "L", 8433 + "id": "V_L", 8434 + "weight": 0.5 8435 + }, 8436 + { 8437 + "a": "V", 8438 + "b": "M", 8439 + "id": "V_M", 8440 + "weight": 0.5 8441 + }, 8442 + { 8443 + "a": "V", 8444 + "b": "N", 8445 + "id": "V_N", 8446 + "weight": 0.5 8447 + }, 8448 + { 8449 + "a": "V", 8450 + "b": "NG", 8451 + "id": "V_NG", 8452 + "weight": 0.5 8453 + }, 8454 + { 8455 + "a": "V", 8456 + "b": "P", 8457 + "id": "V_P", 8458 + "weight": 0.5 8459 + }, 8460 + { 8461 + "a": "V", 8462 + "b": "R", 8463 + "id": "V_R", 8464 + "weight": 0.5 8465 + }, 8466 + { 8467 + "a": "V", 8468 + "b": "S", 8469 + "id": "V_S", 8470 + "weight": 0.5 8471 + }, 8472 + { 8473 + "a": "V", 8474 + "b": "SH", 8475 + "id": "V_SH", 8476 + "weight": 0.5 8477 + }, 8478 + { 8479 + "a": "V", 8480 + "b": "T", 8481 + "id": "V_T", 8482 + "weight": 0.5 8483 + }, 8484 + { 8485 + "a": "V", 8486 + "b": "TH", 8487 + "id": "V_TH", 8488 + "weight": 0.5 8489 + }, 8490 + { 8491 + "a": "V", 8492 + "b": "V", 8493 + "id": "V_V", 8494 + "weight": 0.5 8495 + }, 8496 + { 8497 + "a": "V", 8498 + "b": "W", 8499 + "id": "V_W", 8500 + "weight": 0.5 8501 + }, 8502 + { 8503 + "a": "V", 8504 + "b": "Y", 8505 + "id": "V_Y", 8506 + "weight": 0.5 8507 + }, 8508 + { 8509 + "a": "V", 8510 + "b": "Z", 8511 + "id": "V_Z", 8512 + "weight": 0.5 8513 + }, 8514 + { 8515 + "a": "W", 8516 + "b": "AA", 8517 + "id": "W_AA", 8518 + "weight": 0.5 8519 + }, 8520 + { 8521 + "a": "W", 8522 + "b": "AE", 8523 + "id": "W_AE", 8524 + "weight": 0.5 8525 + }, 8526 + { 8527 + "a": "W", 8528 + "b": "AH", 8529 + "id": "W_AH", 8530 + "weight": 0.5 8531 + }, 8532 + { 8533 + "a": "W", 8534 + "b": "AO", 8535 + "id": "W_AO", 8536 + "weight": 0.5 8537 + }, 8538 + { 8539 + "a": "W", 8540 + "b": "AW", 8541 + "id": "W_AW", 8542 + "weight": 0.5 8543 + }, 8544 + { 8545 + "a": "W", 8546 + "b": "AY", 8547 + "id": "W_AY", 8548 + "weight": 0.5 8549 + }, 8550 + { 8551 + "a": "W", 8552 + "b": "B", 8553 + "id": "W_B", 8554 + "weight": 0.5 8555 + }, 8556 + { 8557 + "a": "W", 8558 + "b": "CH", 8559 + "id": "W_CH", 8560 + "weight": 0.5 8561 + }, 8562 + { 8563 + "a": "W", 8564 + "b": "D", 8565 + "id": "W_D", 8566 + "weight": 0.5 8567 + }, 8568 + { 8569 + "a": "W", 8570 + "b": "DH", 8571 + "id": "W_DH", 8572 + "weight": 0.5 8573 + }, 8574 + { 8575 + "a": "W", 8576 + "b": "EH", 8577 + "id": "W_EH", 8578 + "weight": 0.5 8579 + }, 8580 + { 8581 + "a": "W", 8582 + "b": "ER", 8583 + "id": "W_ER", 8584 + "weight": 0.5 8585 + }, 8586 + { 8587 + "a": "W", 8588 + "b": "EY", 8589 + "id": "W_EY", 8590 + "weight": 0.5 8591 + }, 8592 + { 8593 + "a": "W", 8594 + "b": "F", 8595 + "id": "W_F", 8596 + "weight": 0.5 8597 + }, 8598 + { 8599 + "a": "W", 8600 + "b": "G", 8601 + "id": "W_G", 8602 + "weight": 0.5 8603 + }, 8604 + { 8605 + "a": "W", 8606 + "b": "IH", 8607 + "id": "W_IH", 8608 + "weight": 0.5 8609 + }, 8610 + { 8611 + "a": "W", 8612 + "b": "IY", 8613 + "id": "W_IY", 8614 + "weight": 0.5 8615 + }, 8616 + { 8617 + "a": "W", 8618 + "b": "JH", 8619 + "id": "W_JH", 8620 + "weight": 0.5 8621 + }, 8622 + { 8623 + "a": "W", 8624 + "b": "K", 8625 + "id": "W_K", 8626 + "weight": 0.5 8627 + }, 8628 + { 8629 + "a": "W", 8630 + "b": "L", 8631 + "id": "W_L", 8632 + "weight": 0.5 8633 + }, 8634 + { 8635 + "a": "W", 8636 + "b": "M", 8637 + "id": "W_M", 8638 + "weight": 0.5 8639 + }, 8640 + { 8641 + "a": "W", 8642 + "b": "N", 8643 + "id": "W_N", 8644 + "weight": 0.5 8645 + }, 8646 + { 8647 + "a": "W", 8648 + "b": "NG", 8649 + "id": "W_NG", 8650 + "weight": 0.5 8651 + }, 8652 + { 8653 + "a": "W", 8654 + "b": "OW", 8655 + "id": "W_OW", 8656 + "weight": 0.5 8657 + }, 8658 + { 8659 + "a": "W", 8660 + "b": "OY", 8661 + "id": "W_OY", 8662 + "weight": 0.5 8663 + }, 8664 + { 8665 + "a": "W", 8666 + "b": "P", 8667 + "id": "W_P", 8668 + "weight": 0.5 8669 + }, 8670 + { 8671 + "a": "W", 8672 + "b": "R", 8673 + "id": "W_R", 8674 + "weight": 0.5 8675 + }, 8676 + { 8677 + "a": "W", 8678 + "b": "S", 8679 + "id": "W_S", 8680 + "weight": 0.5 8681 + }, 8682 + { 8683 + "a": "W", 8684 + "b": "SH", 8685 + "id": "W_SH", 8686 + "weight": 0.5 8687 + }, 8688 + { 8689 + "a": "W", 8690 + "b": "T", 8691 + "id": "W_T", 8692 + "weight": 0.5 8693 + }, 8694 + { 8695 + "a": "W", 8696 + "b": "TH", 8697 + "id": "W_TH", 8698 + "weight": 0.5 8699 + }, 8700 + { 8701 + "a": "W", 8702 + "b": "UH", 8703 + "id": "W_UH", 8704 + "weight": 0.5 8705 + }, 8706 + { 8707 + "a": "W", 8708 + "b": "UW", 8709 + "id": "W_UW", 8710 + "weight": 0.5 8711 + }, 8712 + { 8713 + "a": "W", 8714 + "b": "V", 8715 + "id": "W_V", 8716 + "weight": 0.5 8717 + }, 8718 + { 8719 + "a": "W", 8720 + "b": "W", 8721 + "id": "W_W", 8722 + "weight": 0.5 8723 + }, 8724 + { 8725 + "a": "W", 8726 + "b": "Y", 8727 + "id": "W_Y", 8728 + "weight": 0.5 8729 + }, 8730 + { 8731 + "a": "W", 8732 + "b": "Z", 8733 + "id": "W_Z", 8734 + "weight": 0.5 8735 + }, 8736 + { 8737 + "a": "Y", 8738 + "b": "AA", 8739 + "id": "Y_AA", 8740 + "weight": 0.5 8741 + }, 8742 + { 8743 + "a": "Y", 8744 + "b": "AE", 8745 + "id": "Y_AE", 8746 + "weight": 0.5 8747 + }, 8748 + { 8749 + "a": "Y", 8750 + "b": "AH", 8751 + "id": "Y_AH", 8752 + "weight": 0.5 8753 + }, 8754 + { 8755 + "a": "Y", 8756 + "b": "AO", 8757 + "id": "Y_AO", 8758 + "weight": 0.5 8759 + }, 8760 + { 8761 + "a": "Y", 8762 + "b": "AW", 8763 + "id": "Y_AW", 8764 + "weight": 0.5 8765 + }, 8766 + { 8767 + "a": "Y", 8768 + "b": "AY", 8769 + "id": "Y_AY", 8770 + "weight": 0.5 8771 + }, 8772 + { 8773 + "a": "Y", 8774 + "b": "B", 8775 + "id": "Y_B", 8776 + "weight": 0.5 8777 + }, 8778 + { 8779 + "a": "Y", 8780 + "b": "CH", 8781 + "id": "Y_CH", 8782 + "weight": 0.5 8783 + }, 8784 + { 8785 + "a": "Y", 8786 + "b": "D", 8787 + "id": "Y_D", 8788 + "weight": 0.5 8789 + }, 8790 + { 8791 + "a": "Y", 8792 + "b": "DH", 8793 + "id": "Y_DH", 8794 + "weight": 0.5 8795 + }, 8796 + { 8797 + "a": "Y", 8798 + "b": "EH", 8799 + "id": "Y_EH", 8800 + "weight": 0.5 8801 + }, 8802 + { 8803 + "a": "Y", 8804 + "b": "ER", 8805 + "id": "Y_ER", 8806 + "weight": 0.5 8807 + }, 8808 + { 8809 + "a": "Y", 8810 + "b": "EY", 8811 + "id": "Y_EY", 8812 + "weight": 0.5 8813 + }, 8814 + { 8815 + "a": "Y", 8816 + "b": "F", 8817 + "id": "Y_F", 8818 + "weight": 0.5 8819 + }, 8820 + { 8821 + "a": "Y", 8822 + "b": "G", 8823 + "id": "Y_G", 8824 + "weight": 0.5 8825 + }, 8826 + { 8827 + "a": "Y", 8828 + "b": "IH", 8829 + "id": "Y_IH", 8830 + "weight": 0.5 8831 + }, 8832 + { 8833 + "a": "Y", 8834 + "b": "IY", 8835 + "id": "Y_IY", 8836 + "weight": 0.5 8837 + }, 8838 + { 8839 + "a": "Y", 8840 + "b": "JH", 8841 + "id": "Y_JH", 8842 + "weight": 0.5 8843 + }, 8844 + { 8845 + "a": "Y", 8846 + "b": "K", 8847 + "id": "Y_K", 8848 + "weight": 0.5 8849 + }, 8850 + { 8851 + "a": "Y", 8852 + "b": "L", 8853 + "id": "Y_L", 8854 + "weight": 0.5 8855 + }, 8856 + { 8857 + "a": "Y", 8858 + "b": "M", 8859 + "id": "Y_M", 8860 + "weight": 0.5 8861 + }, 8862 + { 8863 + "a": "Y", 8864 + "b": "N", 8865 + "id": "Y_N", 8866 + "weight": 0.5 8867 + }, 8868 + { 8869 + "a": "Y", 8870 + "b": "NG", 8871 + "id": "Y_NG", 8872 + "weight": 0.5 8873 + }, 8874 + { 8875 + "a": "Y", 8876 + "b": "OW", 8877 + "id": "Y_OW", 8878 + "weight": 0.5 8879 + }, 8880 + { 8881 + "a": "Y", 8882 + "b": "OY", 8883 + "id": "Y_OY", 8884 + "weight": 0.5 8885 + }, 8886 + { 8887 + "a": "Y", 8888 + "b": "P", 8889 + "id": "Y_P", 8890 + "weight": 0.5 8891 + }, 8892 + { 8893 + "a": "Y", 8894 + "b": "R", 8895 + "id": "Y_R", 8896 + "weight": 0.5 8897 + }, 8898 + { 8899 + "a": "Y", 8900 + "b": "S", 8901 + "id": "Y_S", 8902 + "weight": 0.5 8903 + }, 8904 + { 8905 + "a": "Y", 8906 + "b": "SH", 8907 + "id": "Y_SH", 8908 + "weight": 0.5 8909 + }, 8910 + { 8911 + "a": "Y", 8912 + "b": "T", 8913 + "id": "Y_T", 8914 + "weight": 0.5 8915 + }, 8916 + { 8917 + "a": "Y", 8918 + "b": "TH", 8919 + "id": "Y_TH", 8920 + "weight": 0.5 8921 + }, 8922 + { 8923 + "a": "Y", 8924 + "b": "UH", 8925 + "id": "Y_UH", 8926 + "weight": 0.5 8927 + }, 8928 + { 8929 + "a": "Y", 8930 + "b": "UW", 8931 + "id": "Y_UW", 8932 + "weight": 0.5 8933 + }, 8934 + { 8935 + "a": "Y", 8936 + "b": "V", 8937 + "id": "Y_V", 8938 + "weight": 0.5 8939 + }, 8940 + { 8941 + "a": "Y", 8942 + "b": "W", 8943 + "id": "Y_W", 8944 + "weight": 0.5 8945 + }, 8946 + { 8947 + "a": "Y", 8948 + "b": "Y", 8949 + "id": "Y_Y", 8950 + "weight": 0.5 8951 + }, 8952 + { 8953 + "a": "Y", 8954 + "b": "Z", 8955 + "id": "Y_Z", 8956 + "weight": 0.5 8957 + }, 8958 + { 8959 + "a": "Z", 8960 + "b": "B", 8961 + "id": "Z_B", 8962 + "weight": 0.5 8963 + }, 8964 + { 8965 + "a": "Z", 8966 + "b": "CH", 8967 + "id": "Z_CH", 8968 + "weight": 0.5 8969 + }, 8970 + { 8971 + "a": "Z", 8972 + "b": "D", 8973 + "id": "Z_D", 8974 + "weight": 0.5 8975 + }, 8976 + { 8977 + "a": "Z", 8978 + "b": "DH", 8979 + "id": "Z_DH", 8980 + "weight": 0.5 8981 + }, 8982 + { 8983 + "a": "Z", 8984 + "b": "F", 8985 + "id": "Z_F", 8986 + "weight": 0.5 8987 + }, 8988 + { 8989 + "a": "Z", 8990 + "b": "G", 8991 + "id": "Z_G", 8992 + "weight": 0.5 8993 + }, 8994 + { 8995 + "a": "Z", 8996 + "b": "JH", 8997 + "id": "Z_JH", 8998 + "weight": 0.5 8999 + }, 9000 + { 9001 + "a": "Z", 9002 + "b": "K", 9003 + "id": "Z_K", 9004 + "weight": 0.5 9005 + }, 9006 + { 9007 + "a": "Z", 9008 + "b": "L", 9009 + "id": "Z_L", 9010 + "weight": 0.5 9011 + }, 9012 + { 9013 + "a": "Z", 9014 + "b": "M", 9015 + "id": "Z_M", 9016 + "weight": 0.5 9017 + }, 9018 + { 9019 + "a": "Z", 9020 + "b": "N", 9021 + "id": "Z_N", 9022 + "weight": 0.5 9023 + }, 9024 + { 9025 + "a": "Z", 9026 + "b": "NG", 9027 + "id": "Z_NG", 9028 + "weight": 0.5 9029 + }, 9030 + { 9031 + "a": "Z", 9032 + "b": "P", 9033 + "id": "Z_P", 9034 + "weight": 0.5 9035 + }, 9036 + { 9037 + "a": "Z", 9038 + "b": "R", 9039 + "id": "Z_R", 9040 + "weight": 0.5 9041 + }, 9042 + { 9043 + "a": "Z", 9044 + "b": "S", 9045 + "id": "Z_S", 9046 + "weight": 0.5 9047 + }, 9048 + { 9049 + "a": "Z", 9050 + "b": "SH", 9051 + "id": "Z_SH", 9052 + "weight": 0.5 9053 + }, 9054 + { 9055 + "a": "Z", 9056 + "b": "T", 9057 + "id": "Z_T", 9058 + "weight": 0.5 9059 + }, 9060 + { 9061 + "a": "Z", 9062 + "b": "TH", 9063 + "id": "Z_TH", 9064 + "weight": 0.5 9065 + }, 9066 + { 9067 + "a": "Z", 9068 + "b": "V", 9069 + "id": "Z_V", 9070 + "weight": 0.5 9071 + }, 9072 + { 9073 + "a": "Z", 9074 + "b": "W", 9075 + "id": "Z_W", 9076 + "weight": 0.5 9077 + }, 9078 + { 9079 + "a": "Z", 9080 + "b": "Y", 9081 + "id": "Z_Y", 9082 + "weight": 0.5 9083 + }, 9084 + { 9085 + "a": "Z", 9086 + "b": "Z", 9087 + "id": "Z_Z", 9088 + "weight": 0.5 9089 + }, 9090 + { 9091 + "a": "ZH", 9092 + "b": "B", 9093 + "id": "ZH_B", 9094 + "weight": 0.5 9095 + }, 9096 + { 9097 + "a": "ZH", 9098 + "b": "CH", 9099 + "id": "ZH_CH", 9100 + "weight": 0.5 9101 + }, 9102 + { 9103 + "a": "ZH", 9104 + "b": "D", 9105 + "id": "ZH_D", 9106 + "weight": 0.5 9107 + }, 9108 + { 9109 + "a": "ZH", 9110 + "b": "DH", 9111 + "id": "ZH_DH", 9112 + "weight": 0.5 9113 + }, 9114 + { 9115 + "a": "ZH", 9116 + "b": "F", 9117 + "id": "ZH_F", 9118 + "weight": 0.5 9119 + }, 9120 + { 9121 + "a": "ZH", 9122 + "b": "G", 9123 + "id": "ZH_G", 9124 + "weight": 0.5 9125 + }, 9126 + { 9127 + "a": "ZH", 9128 + "b": "JH", 9129 + "id": "ZH_JH", 9130 + "weight": 0.5 9131 + }, 9132 + { 9133 + "a": "ZH", 9134 + "b": "K", 9135 + "id": "ZH_K", 9136 + "weight": 0.5 9137 + }, 9138 + { 9139 + "a": "ZH", 9140 + "b": "L", 9141 + "id": "ZH_L", 9142 + "weight": 0.5 9143 + }, 9144 + { 9145 + "a": "ZH", 9146 + "b": "M", 9147 + "id": "ZH_M", 9148 + "weight": 0.5 9149 + }, 9150 + { 9151 + "a": "ZH", 9152 + "b": "N", 9153 + "id": "ZH_N", 9154 + "weight": 0.5 9155 + }, 9156 + { 9157 + "a": "ZH", 9158 + "b": "NG", 9159 + "id": "ZH_NG", 9160 + "weight": 0.5 9161 + }, 9162 + { 9163 + "a": "ZH", 9164 + "b": "P", 9165 + "id": "ZH_P", 9166 + "weight": 0.5 9167 + }, 9168 + { 9169 + "a": "ZH", 9170 + "b": "R", 9171 + "id": "ZH_R", 9172 + "weight": 0.5 9173 + }, 9174 + { 9175 + "a": "ZH", 9176 + "b": "S", 9177 + "id": "ZH_S", 9178 + "weight": 0.5 9179 + }, 9180 + { 9181 + "a": "ZH", 9182 + "b": "SH", 9183 + "id": "ZH_SH", 9184 + "weight": 0.5 9185 + }, 9186 + { 9187 + "a": "ZH", 9188 + "b": "T", 9189 + "id": "ZH_T", 9190 + "weight": 0.5 9191 + }, 9192 + { 9193 + "a": "ZH", 9194 + "b": "TH", 9195 + "id": "ZH_TH", 9196 + "weight": 0.5 9197 + }, 9198 + { 9199 + "a": "ZH", 9200 + "b": "V", 9201 + "id": "ZH_V", 9202 + "weight": 0.5 9203 + }, 9204 + { 9205 + "a": "ZH", 9206 + "b": "W", 9207 + "id": "ZH_W", 9208 + "weight": 0.5 9209 + }, 9210 + { 9211 + "a": "ZH", 9212 + "b": "Y", 9213 + "id": "ZH_Y", 9214 + "weight": 0.5 9215 + }, 9216 + { 9217 + "a": "ZH", 9218 + "b": "Z", 9219 + "id": "ZH_Z", 9220 + "weight": 0.5 9221 + } 9222 + ] 9223 + }
+110
pop/voice/jeffrey-anthropometry.json
··· 1 + { 2 + "version": 1, 3 + "generated": "2026-05-04", 4 + "generator": "pop/voice/bin/measure-jeffrey.py v0.2", 5 + "source_glob": "/Users/jas/aesthetic-computer/portraits/jeffrey/corpus/shoot/*.jpg", 6 + "source_corpus": { 7 + "whistlegraph": 521 8 + }, 9 + "rejected": { 10 + "no_face": 158 11 + }, 12 + "measurements_cm": { 13 + "forehead_to_chin_cm": { 14 + "median": 15.82, 15 + "iqr": [ 16 + 15.18, 17 + 16.78 18 + ], 19 + "n": 363 20 + }, 21 + "bizygomatic_width_cm": { 22 + "median": 12.31, 23 + "iqr": [ 24 + 11.94, 25 + 12.8 26 + ], 27 + "n": 363 28 + }, 29 + "mandible_length_cm": { 30 + "median": 6.38, 31 + "iqr": [ 32 + 5.77, 33 + 7.43 34 + ], 35 + "n": 363 36 + }, 37 + "lip_width_cm": { 38 + "median": 4.99, 39 + "iqr": [ 40 + 4.67, 41 + 5.41 42 + ], 43 + "n": 363 44 + }, 45 + "lip_aperture_cm": { 46 + "median": 2.02, 47 + "iqr": [ 48 + 1.61, 49 + 2.68 50 + ], 51 + "n": 363 52 + }, 53 + "philtrum_cm": { 54 + "median": 2.06, 55 + "iqr": [ 56 + 1.82, 57 + 2.25 58 + ], 59 + "n": 363 60 + }, 61 + "nose_to_chin_cm": { 62 + "median": 6.72, 63 + "iqr": [ 64 + 6.23, 65 + 7.32 66 + ], 67 + "n": 363 68 + }, 69 + "nostril_width_cm": { 70 + "median": 2.95, 71 + "iqr": [ 72 + 2.77, 73 + 3.16 74 + ], 75 + "n": 363 76 + } 77 + }, 78 + "derived": { 79 + "vtl_cm": { 80 + "central": 17.5, 81 + "ci95": [ 82 + 15.93, 83 + 19.07 84 + ], 85 + "model": "fitch-giedd-1999", 86 + "input": "population-median-male" 87 + }, 88 + "tract_segments_44": { 89 + "segment_length_cm": 0.3977 90 + }, 91 + "lip_aperture_max_cm": 2.68, 92 + "lip_mul_upper_bound": 1.0, 93 + "mandible_rotation_max_deg": 22, 94 + "notes": [ 95 + "VTL central uses Fitch+Giedd 1999 body-height regression.", 96 + "Photo-derived measurements (forehead-to-chin, bizygomatic, mandible) are not yet wired into a closed-form VTL \u2014 Lammert-Narayanan 2015 needs MRI-derived inputs we don't have.", 97 + "Lip aperture upper bound comes from corpus 75th percentile (max would be too sensitive to selfie outliers)." 98 + ] 99 + }, 100 + "scale_anchor": { 101 + "method": "interpupillary distance", 102 + "ipd_cm_used": 6.3, 103 + "ipd_px_median": 120.8, 104 + "rationale": "Adult-population mean (Dodgson 2004). Replace with stated height + Lammert 2015 if/when --height-cm is provided." 105 + }, 106 + "notes": [ 107 + "Single-pass mediapipe FaceMesh on local AV-shoot cache.", 108 + "Identity validation skipped (AV shoot is jeffrey by construction). Wire --identity-jsonl when running against IG archive." 109 + ] 110 + }