gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 188Rust 55Shell 30Markdown 281211 Sutichaykuna

receta: la distro HABLA — espeak-ng y su salida de audio

`censo-distro.py` marcaba «accesibilidad» **4 de 4 ausente**, y es el hueco que deja gente afuera de
verdad: sin voz ni lector de pantalla una distro no es incómoda para quien no ve, es **inusable**. Y
ninguna otra métrica del repo lo ve — el tablero puede estar en 1158/1158 y clausura 10 de 10
mientras la máquina es muda.

`espeak-ng` es el eslabón de abajo y vale por sí solo: 100+ idiomas por síntesis formante, sin
modelos ni red, en unos MB. Lo usan `orca` y `speech-dispatcher`, y también alguien a mano
(`espeak-ng -v es "hola"`).

⚠ **LOS DOS GUARDIANES SON EL PUNTO DE LA RECETA, no un adorno.** espeak-ng busca TODAS sus deps
con `find_library`/`find_path` **sin REQUIRED** (`cmake/deps.cmake`), así que construye y sella sin
ninguna — y entonces sintetiza a un `.wav` y **no reproduce**: `espeak-ng "hola"` no hace ruido. Es
«sellado, con contenido, reproducible y sin la función que promete» aplicado al caso donde la
promesa es literalmente que la máquina hable. El `install` comprueba (1) que el binario traiga
salida de audio y (2) que los datos de voz estén (>50 ficheros).

Y `pcaudiolib` entra como receta propia por lo mismo: es lo que convierte «sintetiza» en «suena».
Su guardián exige **al menos un backend detectado** leyendo su `config.log` — una pcaudiolib sin
pulse ni alsa compila, instala y no reproduce, y el problema se trasladaría a espeak-ng disfrazado
de otra cosa. Se dejan los dos backends: el servidor de audio de esta distro es PipeWire, que habla
el protocolo de PulseAudio, y ALSA queda como camino directo sin servidor (un instalador, una
consola).

⚠ **Y `pcaudiolib` usa el tarball del RELEASE, no el archive del tag — eso evita dos recetas.** El
archive de un tag trae sólo `configure.ac` ⇒ haría falta `autogen.sh` y con él **autoconf y
automake, que no están en el corpus** (el censo los tiene como hueco; `libtool` y `m4` sí están). El
asset del release es un `make dist` con `configure` y `Makefile.in` generados: 384 KB contra 36 KB,
y ahí está la diferencia. Comprobado antes de cambiarlo. **La lección general: cuando un proyecto
autotools no construye por falta de autoconf, mirar si publica un tarball de release antes de
escribir el autoconf.**

Y `binutils` está en las deps de espeak-ng por los guardianes, no por el build: aportan `readelf` y
`strings`. Sin ellos el control de audio fallaría por falta de herramienta en vez de por el defecto
que busca — un guardián que se cae solo acusa a la receta de algo que no hizo, y eso ya me pasó hoy
con el `hangul.so`.

⚠ Lo que esto NO cierra: **`orca`** (el lector de pantalla) necesita `at-spi2-core` y `py3-gobject`,
los dos sólo en `incoming-gnome` — la misma pila de introspección que bloqueó a `librsvg`, con el
mismo coste medido. Queda como decisión, igual que el chino.

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

3130016927a7d2613298687c350d113a6a38ed00 · ruwaq Sergio · (2 p'unchaw ñawpaq) · tayta 938f90ff3 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

2 qillqakuna · +159 −0

diff --git a/recipes/espeak-ng.toml b/recipes/espeak-ng.toml
new file mode 100644
index 00000000..162b232c
--- /dev/null
+++ b/recipes/espeak-ng.toml
@@ -0,0 +1,90 @@
+# espeak-ng 1.52.0 — el sintetizador de voz. Con esto la distro HABLA.
+#
+# ══ POR QUÉ ENTRA: ES EL HUECO QUE DEJA GENTE AFUERA ═══════════════════════════════════════════
+# `censo-distro.py` marcaba «accesibilidad» **4 de 4 ausente**: sin lector de pantalla ni voz, una
+# distro es inusable para quien no ve — no incómoda, inusable. Y no hay forma de medir eso con las
+# otras métricas del repo: el tablero puede estar en 1158/1158 y clausura 10 de 10 mientras la
+# máquina es muda.
+#
+# espeak-ng es el eslabón de abajo y el que vale por sí solo: 100+ idiomas por síntesis formante,
+# sin modelos ni red, en unos pocos MB. Es lo que usan `orca` y `speech-dispatcher`, y también lo
+# que alguien invoca a mano (`espeak-ng -v es "hola"`).
+#
+# ══ ⚠ LOS DOS GUARDIANES SON EL PUNTO DE ESTA RECETA ═══════════════════════════════════════════
+# espeak-ng busca TODAS sus deps con `find_library`/`find_path` **sin REQUIRED**
+# (`cmake/deps.cmake`): sonic, pcaudio, mbrola. O sea que **construye y sella sin ninguna**, y
+# entonces:
+#   · sin `pcaudio` → sintetiza a un `.wav` y **no reproduce**. `espeak-ng "hola"` no hace ruido.
+#   · sin los datos de voz compilados → arranca y no tiene con qué pronunciar.
+# Las dos cosas son «sellado, con contenido, reproducible y sin la función que promete», que es la
+# figura que este catálogo persigue; en accesibilidad es peor, porque la promesa es que la máquina
+# habla. ⇒ el `install` comprueba que el binario SÍ enlace pcaudio y que los datos estén.
+#
+# ══ LAS PERILLAS ═══════════════════════════════════════════════════════════════════════════════
+#   BUILD_SHARED_LIBS=ON   `speech-dispatcher` y `orca` cargan `libespeak-ng.so` con dlopen; el
+#                          default de upstream es OFF, que dejaría sólo la `.a`
+#   USE_MBROLA=OFF         voces MBROLA: binario externo, no está en el corpus y son diccionarios
+#                          por idioma aparte. La síntesis formante propia no las necesita
+#   USE_SPEECHPLAYER=OFF   sintetizador alternativo experimental
+#   ESPEAK_COMPAT=ON       instala también el nombre viejo `espeak`, que es el que invocan los
+#                          scripts y las apps que no se actualizaron a `espeak-ng`
+name    = "espeak-ng"
+version = "1.52.0"
+license = "GPL-3.0-or-later"
+
+[source]
+tarball = "https://github.com/espeak-ng/espeak-ng/archive/refs/tags/1.52.0/espeak-ng-1.52.0.tar.gz"
+sha256  = "bb4338102ff3b49a81423da8a1a158b420124b055b60fa76cfb4b18677130a23"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "dynamic"
+flags    = []
+
+[build.phases]
+configure = '''
+ZW="$PWD/.zwrap"; mkdir -p "$ZW"
+# El envoltorio reescribe `--target`: patrón canónico del repo (30 recetas) para builds que inventan
+# su propio triple y hacen que zig corte con «UnknownOperatingSystem».
+printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec /opt/zig/zig cc -mcpu=baseline "$@"' > "$ZW/cc"
+printf '%s\n' '#!/bin/sh' 'for a do' 'case "$a" in --target=*) a=--target=x86_64-linux-musl ;; esac' 'set -- "$@" "$a"' 'shift' 'done' 'exec /opt/zig/zig c++ -mcpu=baseline "$@"' > "$ZW/cxx"
+chmod +x "$ZW/cc" "$ZW/cxx"
+rm -rf build
+cmake -B build -G Ninja -Wno-dev \
+  -DCMAKE_C_COMPILER="$ZW/cc" -DCMAKE_CXX_COMPILER="$ZW/cxx" \
+  -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
+  -DBUILD_SHARED_LIBS=ON -DUSE_MBROLA=OFF -DUSE_SPEECHPLAYER=OFF \
+  -DESPEAK_COMPAT=ON -DBUILD_TESTING=OFF
+'''
+compile = "cmake --build build"
+install = '''
+set -e
+DESTDIR=/out cmake --install build
+[ -x /out/usr/bin/espeak-ng ] || { echo "espeak-ng: no quedó el binario — NO sello" >&2; exit 1; }
+so=$(find /out/usr/lib -name 'libespeak-ng.so*' 2>/dev/null | head -1)
+[ -n "$so" ] || { echo "espeak-ng: no quedó libespeak-ng.so — orca y speech-dispatcher la cargan con dlopen" >&2; exit 1; }
+
+# ⚠ GUARDIÁN 1: que REPRODUZCA, no sólo que sintetice. `pcaudio` es opcional para cmake, así que
+# sin este control se sellaría un espeak-ng mudo — compila, instala, y `espeak-ng "hola"` no suena.
+if ! readelf -d /out/usr/bin/espeak-ng 2>/dev/null | grep -qi 'pcaudio' \
+   && ! readelf -d "$so" 2>/dev/null | grep -qi 'pcaudio' \
+   && ! strings -a /out/usr/bin/espeak-ng 2>/dev/null | grep -q 'pcaudiolib\|pulse_simple\|snd_pcm_open'; then
+  echo "espeak-ng: el binario NO trae salida de audio (pcaudio ausente) — sintetizaría a fichero y no sonaría" >&2
+  echo "           pcaudiolib es dep OPCIONAL para su cmake: si no la encontró, siguió sin ella en silencio" >&2
+  exit 1
+fi
+
+# ⚠ GUARDIÁN 2: los datos de voz. Se compilan desde la fuente (`cmake/data.cmake`), no se bajan —
+# pero si la fase de datos se saltara, quedaría un sintetizador sin fonemas que no pronuncia nada.
+d=$(find /out/usr/share/espeak-ng-data -type f 2>/dev/null | wc -l)
+[ "$d" -gt 50 ] || { echo "espeak-ng: sólo $d fichero(s) en espeak-ng-data — esperaba >50; sin fonemas ni voces no pronuncia" >&2; exit 1; }
+v=$(ls /out/usr/share/espeak-ng-data/lang 2>/dev/null | wc -l)
+echo "espeak-ng: binario + libespeak-ng.so + $d fichero(s) de datos · $v familia(s) de idioma"
+'''
+
+[deps]
+# ⚠ `binutils` está acá por los GUARDIANES, no por el build: aportan `readelf` y `strings`, y sin
+# ellos el control de audio fallaría por falta de herramienta en vez de por el defecto que busca —
+# un guardián que se cae solo acusa a la receta de algo que no hizo.
+build = ["cmake", "samurai", "pkgconf", "pcaudiolib", "pulseaudio", "alsa-lib", "binutils"]
diff --git a/recipes/pcaudiolib.toml b/recipes/pcaudiolib.toml
new file mode 100644
index 00000000..81b8c711
--- /dev/null
+++ b/recipes/pcaudiolib.toml
@@ -0,0 +1,69 @@
+# pcaudiolib 1.3 — la capa de salida de audio de espeak-ng. Sin esto, la distro no SUENA.
+#
+# ══ POR QUÉ ES SU PROPIA RECETA Y NO UN DETALLE DE espeak-ng ═══════════════════════════════════
+# `espeak-ng` la busca con `find_library(PCAUDIO_LIB pcaudio)` — **opcional, sin REQUIRED**
+# (`cmake/deps.cmake:3`). O sea que espeak-ng construye sin ella y sella tranquilamente… y entonces
+# sintetiza a un `.wav` y **no puede reproducir**: `espeak-ng "hola"` no hace ruido.
+#
+# Eso es exactamente la figura que este catálogo persigue —sellado, con contenido, reproducible y
+# sin la función que promete— y en el frente de accesibilidad es peor que en otros: la promesa es
+# «la máquina habla», no «la máquina genera un fichero de audio».
+#
+# ⇒ Entra como dep de espeak-ng, cuyo guardián comprueba que el binario resultante SÍ la enlace.
+#
+# ══ LOS BACKENDS ═══════════════════════════════════════════════════════════════════════════════
+# `configure.ac` ofrece PulseAudio (`libpulse-simple`) y ALSA, los dos con `--with-*` y default
+# `yes`. Los dos están en el corpus, así que se dejan los dos encendidos: **el servidor de audio de
+# esta distro es PipeWire**, que habla el protocolo de PulseAudio por su capa de compatibilidad, y
+# ALSA queda como camino directo si alguien corre sin servidor (un instalador, una consola).
+#
+# `--without-sndio`: no está en el corpus y es el camino de OpenBSD.
+name    = "pcaudiolib"
+version = "1.3"
+license = "GPL-3.0-or-later"
+
+[source]
+tarball = "https://github.com/espeak-ng/pcaudiolib/releases/download/1.3/pcaudiolib-1.3.tar.gz"
+sha256  = "e8bd15f460ea171ccd0769ea432e188532a7fb27fa73ec2d526088a082abaaad"
+
+[build]
+compiler = "zig-cc"
+target   = "x86_64-linux-musl"
+link     = "static"
+flags    = []
+
+[build.phases]
+# ⚠ **SE USA EL TARBALL DEL RELEASE, NO EL ARCHIVE DEL TAG — y eso evita dos recetas.**
+# El archive de un tag (`/archive/refs/tags/…`) trae sólo `configure.ac`, así que haría falta
+# `autogen.sh` y con él **autoconf y automake, que NO están en el corpus** (el censo los tiene como
+# hueco; `libtool` y `m4` sí están). El asset del release (`/releases/download/…`) es un `make dist`
+# con `configure` y `Makefile.in` ya generados — 384 KB contra 36 KB del tag, y ahí está la
+# diferencia. Comprobado antes de cambiarlo: el tarball trae `pcaudiolib-1.3/configure`.
+#
+# La lección general: **cuando un proyecto autotools no construye por falta de autoconf, mirar si
+# publica un tarball de release antes de escribir el autoconf.** No siempre existe, pero cuando
+# existe ahorra una campaña.
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --disable-shared --enable-static --with-pic --without-sndio'
+compile   = 'make -j"$(nproc)"'
+# Guardián: la `.a`, su `.pc` —que es como espeak-ng la encuentra— y **al menos un backend de
+# audio compilado**. Lo último es el punto: una pcaudiolib sin pulse ni alsa compila, instala y no
+# reproduce nada, y entonces el problema se traslada a espeak-ng disfrazado de otra cosa.
+install = '''
+set -e
+make DESTDIR=/out install
+[ -f /out/usr/lib/libpcaudio.a ] || { echo "pcaudiolib: no quedó la .a — NO sello" >&2; exit 1; }
+[ -f /out/usr/lib/pkgconfig/pcaudiolib.pc ] || { echo "pcaudiolib: falta pcaudiolib.pc — espeak-ng la busca por ahí" >&2; exit 1; }
+# `config.log` dice qué backends detectó el configure; si ninguno, esta receta no sirve para nada.
+back=""
+grep -q 'have_pulseaudio=.\{0,1\}yes' config.log 2>/dev/null && back="$back pulse"
+grep -q 'have_alsa=.\{0,1\}yes' config.log 2>/dev/null && back="$back alsa"
+if [ -z "$back" ]; then
+  echo "pcaudiolib: CERO backends de audio detectados — compilaría y no reproduciría nada" >&2
+  grep -iE 'have_(pulseaudio|alsa)=' config.log 2>/dev/null | head -4 >&2
+  exit 1
+fi
+echo "pcaudiolib: .a + .pc · backends:$back"
+'''
+
+[deps]
+build = ["libtool", "make", "pkgconf", "pulseaudio", "alsa-lib"]