@ -1,11 +1,12 @@
< script lang = "ts" >
/**
* `/uix/docs/sound` — the entry point for the sound channel.
* `/uix/docs/sound` — the sound channel, end to end .
*
* Written for someone who has never seen this framework: it introduces the
* vocabulary (family, verb, intent) BEFORE using it, and only names the
* machinery once the reader needs the word. Tables are derived from the
* live `SEMA_MAP` and the real resolver, so they cannot go stale.
* For a developer who has never seen this framework: it goes from WHY a UI
* makes sound at all, through where an event is declared (morfo), who fires
* it (soma), who chooses the sound (sema) and how the sound is actually
* MADE (the Web Audio graph), to the policies that decide whether it is
* heard at all. Values come from the live map and the real resolver.
*/
import { getActiveUix } from '$active-uix';
import { SEMA_MAP , resolveSignature } from '$uix/sema';
@ -56,111 +57,259 @@
).sound;
});
function play(name?: string ) {
function play() {
if (!stage) return;
void uix.events?.emit({
name: 'probe',
family: family as never,
target: stage,
...(verb ? { verb } : {} ),
...(name ? { overrides : { sound : name } } : {} ),
...(intent !== 'neutral' && !name ? { intent : intent as never } : {} )
...(intent !== 'neutral' ? { intent : intent as never } : {} )
});
}
< / script >
< div data-uix-canvas-inner >
< div data-uix-eyebrow > Sema · el canal del sonido< / div >
< h1 data-uix-page-title > Cómo suena una interfaz .< / h1 >
< h1 data-uix-page-title > El sonido, de principio a fin .< / h1 >
< p data-uix-page-lede >
Guardas un documento y oyes un pequeño repique. Lo borras y oyes algo más grave. Nadie escribió
«reproduce este fichero» en el botón de guardar: el componente sólo
< strong > describió lo que estaba pasando< / strong > , y el sistema eligió el sonido. Esta página
explica cómo, desde cero .
«reproduce este fichero» en el botón de guardar. Esta página recorre el camino entero: por qué
una interfaz suena, dónde se declara lo que ocurre, quién elige el sonido, cómo se fabrica ese
sonido, y qué decide si llega a oírse .
< / p >
< div bind:this = { stage } data-uix-sound-stage aria-hidden = "true" ></ div >
<!-- ── 1 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 1 · Un componente describe, no decid e< / h2 >
< h2 data-uix-section-title > 1 · Qué se persigu e< / h2 >
< p data-uix-section-desc >
Cuando algo ocurre en un componente —se pulsa, se guarda, se abre un panel— ese componente < strong
>declara qué clase de suceso es< /strong
>. No dice cómo suena, ni de qué color se pone, ni cómo se anima. Sólo lo describe, con tres
palabras:
El sonido aquí no es un adorno ni una recompensa. Parte de una idea sencilla: cuando algo
ocurre en una interfaz, ocurre < strong > una sola cosa< / strong > , y esa cosa se puede expresar
por varios canales a la vez — se ve, se oye, se siente. El color, el movimiento, el sonido y
la vibración no son cuatro decisiones independientes: son cuatro caras del mismo suceso.
< / p >
< p data-uix-section-desc >
De ahí salen tres objetivos concretos, y conviene tenerlos delante porque explican casi todas
las decisiones raras que verás después:
< / p >
< ul data-uix-prose-list >
< li >
< strong > La familia< / strong > — a qué clase de suceso pertenece. Hay
{ FAMILIES . length } , y son un conjunto cerrado: < code > contact</ code > (algo se ha tocado),
< code > commit< / code >
(algo ha quedado hecho), < code > signal< / code > (el sistema avisa), < code > emerge< / code > (algo
aparece o desaparece),
< code > shift< / code > (el marco cambia), < code > handle< / code > (un gesto continuo), y dos más que no
suenan.
< strong > Que la lectura sobreviva a perder un canal.< / strong > Si alguien tiene el sonido
apagado, o no puede ver bien, o usa un dispositivo sin vibración, la interfaz tiene que
seguir siendo comprensible. Ningún canal lleva información que no esté en otro sitio. El
sonido < em > refuerza< / em > ; no informa en exclusiva.
< / li >
< li >
< strong > El verbo< / strong > — qué se hizo exactamente dentro de esa familia:
< code > save< / code > , < code > delete< / code > , < code > open< / code > , < code > close< / code > …
< strong > Que no fatigue.< / strong > Un sonido que se repite mucho deja de informar y empieza a molestar.
El sistema baja el volumen de lo repetitivo por su cuenta, y muchos sucesos frecuentes directamente
no suenan.
< / li >
< li >
< strong > El intent< / strong > — la carga: ¿salió bien, es arriesgado, es peligroso, se ha
perdido algo? Hay { INTENTS . length } , empezando por < code > neutral</ code > .
< strong > Que sea coherente en toda la aplicación.< / strong > Guardar debería sonar igual en cualquier
pantalla. Eso no se consigue pidiendo disciplina: se consigue quitando a los componentes la posibilidad
de elegir libremente.
< / li >
< / ul >
< p data-uix-section-desc >
Ese último punto es el que da forma a todo lo demás. Un componente < strong > no puede < / strong >
decidir cómo suena, igual que no decide de qué color es un botón primario. Sólo declara
< em > qué está pasando< / em > .
< / p >
< / section >
<!-- ── 2 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 2 · El recorrido completo< / h2 >
< p data-uix-section-desc >
El framework está en capas, y cada una hace una sola cosa. Un sonido atraviesa cuatro. Antes
de entrar en cada una, así se ve el camino entero:
< / p >
< pre data-uix-code > < code
>{ `// El contrato de un componente. No hay ni un sonido aquí.
{
name: 'commit-save',
semantic: { family : 'commit' , verb : 'save' , intent : 'neutral' }
}`}< /code
>{ `morfo declara «este componente puede emitir un commit-toggle» (TypeScript, sin runtime)
soma dispara runtime.trigger('commit-toggle') (el proveedor, al pulsar)
sema elige familia+verbo → un nombre → el intent → un sonido (el motor semántico)
$sound fabrica osciladores → filtro → envolvente → altavoces (Web Audio)`}< /code
>< / pre >
< p data-uix-section-desc >
Esa separación es la idea entera: el componente sabe < em > qué pasó< / em > , y el sistema sabe
< em > a qué suena eso< / em > . Cambiar la voz de toda la aplicación no obliga a tocar ni un
componente.
Cada capa ignora a la siguiente. El morfo no sabe que existe el sonido; sema no sabe qué es un
oscilador; el motor de audio no sabe qué es un «commit». Esa ignorancia mutua es deliberada:
es lo que permite cambiar la voz de la aplicación entera sin tocar un componente, y lo que
permite que el mismo suceso pinte un color en CSS sin que nadie coordine nada a mano.
< / p >
< / section >
<!-- ── 3 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 2 · El sistema elige — dos pasos< / h2 >
< h2 data-uix-section-title > 3 · El morfo — dónde se declara lo que ocurre< / h2 >
< p data-uix-section-desc >
Cada componente de este framework tiene un < strong > morfo< / strong > : un fichero de TypeScript
plano, sin runtime ni dependencias, que describe su contrato — sus partes, sus atributos ARIA,
su mapa de teclado y < strong > los sucesos que puede emitir< / strong > . Vive en
< code > src/uix/morfo/components/{ nombre} .ts< / code >
y es la única fuente de verdad: soma, sema y la capa visual lo leen, nunca al revés.
< / p >
< p data-uix-section-desc >
Esta es la declaración real del suceso de un < code > Toggle< / code > , entera:
< / p >
< pre data-uix-code > < code
>{ `// src/uix/morfo/components/toggle.ts
events: [
{
name: 'commit-toggle',
semantic: {
family: 'commit', // qué clase de suceso es
verb: 'toggle', // qué se hizo exactamente
target: v.partRef('provider'), // sobre qué parte del DOM ocurre
sequence: 'post', // antes o después del cambio de estado
intent: { // la carga, ligada a una prop pública
fromProp: 'intent',
default: 'neutral',
supported: ['neutral', 'affirm', 'risk', 'threat']
}
}
}
]`}< /code
>< / pre >
< p data-uix-section-desc > Las tres palabras que importan para el sonido:< / p >
< ul data-uix-prose-list >
< li >
< strong > < code > family< / code > — a qué clase de suceso pertenece.< / strong > Son
{ FAMILIES . length } y no se pueden inventar más:
< code > contact< / code > (algo se ha tocado), < code > commit< / code > (algo ha quedado hecho),
< code > signal< / code >
(el sistema avisa por su cuenta), < code > emerge< / code >
(algo aparece o desaparece), < code > shift< / code > (cambia el marco: una pestaña, un paso),
< code > handle< / code >
(un gesto continuo), < code > sustain< / code > (algo está en curso) y < code > delegate< / code > (reparto
de iniciativa con el sistema). Las dos últimas no suenan.
< / li >
< li >
< strong > < code > verb< / code > — qué se hizo dentro de esa familia.< / strong > También es un
conjunto cerrado por familia: un < code > commit< / code > puede
< code > save< / code > , < code > delete< / code > , < code > submit< / code > , < code > cancel< / code > … pero no
< code > open< / code > , que es de < code > emerge< / code > .
< / li >
< li >
< strong >< code > intent</ code > — la carga evaluativa.</ strong > Son { INTENTS . length } :
< code > neutral< / code > , < code > affirm< / code > (va bien), < code > fulfill< / code > (salió bien del
todo), < code > risk< / code > (cuidado), < code > threat< / code > (peligro) y
< code > loss< / code > (se perdió algo). Fíjate en que aquí no es un valor fijo:
< code > fromProp: 'intent'< / code > significa que lo decide quien usa el componente, con una prop.
< / li >
< / ul >
< p data-uix-section-desc >
Con esa descripción en la mano, el sistema hace < strong > dos búsquedas< / strong > y nada más.
< strong > En ningún sitio de esa declaración aparece un sonido.< / strong > Ni un fichero, ni un volumen,
ni una nota. El morfo describe el suceso; qué se oye es asunto de otra capa. Esa es exactamente
la separación que hace posible todo lo demás.
< / p >
< / section >
<!-- ── 4 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 4 · Sema — el motor que elige< / h2 >
< p data-uix-section-desc >
< strong > Primero, el nombre.< / strong > Cada familia tiene una tabla que dice a qué suena, y
puede afinar por verbo. Un < code > commit< / code > suena a
< code > tick< / code > ; un < code > emerge< / code > suena a < code > open< / code > , salvo que el verbo sea
< code > close< / code > , en cuyo caso suena a < code > close< / code > . Un componente normal no escribe
nada de esto: ya está en el sistema.
Cuando el usuario pulsa, el proveedor del componente (la capa < strong > soma< / strong > ) dispara
el suceso declarado: < code > runtime.trigger('commit-toggle')< / code > . Eso llega a
< strong > sema< / strong > , el motor semántico, que reparte la señal entre sus
< strong > canales< / strong > :
< / p >
< ul data-uix-prose-list >
< li >
< strong > visual< / strong > — no pinta nada: estampa unos atributos (< code > data-event< / code > ,
< code > data-event-family< / code > ,
< code > data-event-intent< / code > …) en el elemento durante un rato. La capa visual reacciona a
esos atributos desde CSS. Sema no sabe de colores.
< / li >
< li > < strong > sound< / strong > — el que nos ocupa.< / li >
< li >
< strong > haptic< / strong > — la vibración, que funciona igual pero con un vocabulario
categórico (< code > tick< / code > , < code > tap< / code > , < code > pulse< / code > …).
< / li >
< / ul >
< p data-uix-section-desc >
< strong > Después, el intent.< / strong > Con el nombre ya elegido, se busca si existe una versión
de ese sonido para este intent. < code > tick< / code > + < code > threat< / code >
encuentra < code > tick.threat< / code > , que es un sonido distinto: más grave, más rasposo y
descendente. Si esa versión < em > no< / em > existe —por ejemplo
< code > tick.affirm< / code > — el intent simplemente se ignora y suena el
< code > tick< / code > normal. Y si no hubiera ni siquiera < code > tick< / code > , no sonaría nada; el
silencio es una respuesta válida.
Para el sonido, sema hace < strong > dos búsquedas< / strong > y nada más. Primero el
< strong > nombre< / strong > : cada familia tiene una tabla que dice a qué suena, y puede afinarla
por verbo — un < code > commit< / code > suena a < code > tick< / code > ; un
< code > emerge< / code > suena a < code > open< / code > , salvo que el verbo sea
< code > close< / code > . Después el < strong > intent< / strong > : se busca si existe una versión de ese
nombre para esta carga.
< / p >
< pre data-uix-code > < code
>{ `nombre = la tabla de la familia, afinada por verbo
>{ `nombre = familia[verbo] ?? familia.default
sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}< /code
>< / pre >
< p data-uix-section-desc >
Lo importante de este diseño: < strong > nada se deforma< / strong > . Un intent no «hace más grave»
un sonido — < em > elige otro< / em > , diseñado entero por separado. Es como funciona el
reconocimiento: distinguimos sonidos, no desplazamientos de parámetro.
< code > tick< / code > + < code > threat< / code > encuentra < code > tick.threat< / code > , que es
< strong > otro sonido< / strong > : más grave, más áspero y descendente. Si esa versión no existe —< code
>tick.affirm< /code
>, por ejemplo— el intent se ignora y suena el
< code > tick< / code > normal. Si no hubiera ni < code > tick< / code > , no sonaría nada.
< / p >
< p data-uix-section-desc >
El detalle importante: < strong > nada se deforma< / strong > . Un intent no «hace más grave» un
sonido, < em > elige otro< / em > , diseñado entero por separado. Reconocemos sonidos —una puerta, un
cristal, un timbre—, no desplazamientos de parámetro; un sistema que modula un mismo tono
produce variantes que sobre el papel son distintas y al oído son la misma.
< / p >
< / section >
<!-- ── 5 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 3 · Míralo funcionar< / h2 >
< h2 data-uix-section-title > 5 · Cómo se fabrica el sonido < / h2 >
< p data-uix-section-desc >
Elige una familia, un verbo y un intent, y observa las dos búsquedas. Para oírlo, enciende < em
>Sound< /em
>
en el menú < em > Semantics< / em > de la barra superior.
Hasta aquí sema ha elegido una < strong > firma< / strong > : seis números y un contorno. El motor de
audio (< code > $sound< / code > , un artefacto aparte que no sabe nada de semántica) los convierte
en sonido con Web Audio. El grafo es siempre el mismo:
< / p >
< pre data-uix-code > < code
>{ `oscilador 1 (seno, a la altura "pitch") ─┐
oscilador 2 (seno, una quinta arriba) ─┴─→ filtro paso-bajo → envolvente → bus
mezclado al 30 % corte = "centroid" ADSR`}< /code
>< / pre >
< ul data-uix-prose-list >
< li >
< strong > Dos osciladores, no uno.< / strong > Un seno solo suena a pitido de test. El segundo,
una quinta por encima y mezclado al 30 %, le da cuerpo sin volverlo musical. Esa relación es
la < em > voz< / em > del sistema y se puede cambiar entera.
< / li >
< li >
< strong > El filtro es el brillo.< / strong > < code > centroid< / code > es la frecuencia de corte de un
paso-bajo: cuanto más alta, más armónicos pasan y más «cristalino» suena. Es lo que diferencia
un toque de cristal de un golpe sordo, con la misma nota.
< / li >
< li >
< strong > La envolvente da la forma.< / strong > Ataque, mantenimiento y caída. Está limitada a propósito
—el ataque nunca pasa del 15 % de la nota, la caída del 40 %— porque una nota muy corta con envolvente
cuadrada suena a clic digital y borra las diferencias de altura.
< / li >
< li >
< strong > El contorno mueve la altura mientras suena.< / strong > Subiendo, bajando o en arco, con
un barrido de ±400 cents (unos cuatro semitonos). Es suficiente para que un «abre» y un «cierra»
se distingan aunque duren 150 ms.
< / li >
< li >
< strong > La aspereza es un trémolo.< / strong > A partir de 0,2 se enciende una modulación de
amplitud, entre 30 y 180 Hz según lo áspero que sea. Va
< em > en serie< / em > , multiplicando: por eso puede raspar sin subir el nivel. Es lo que hace
que un aviso se oiga como aviso y no como confirmación.
< / li >
< / ul >
< p data-uix-section-desc >
Una entrada del catálogo también puede ser un < strong > fichero< / strong > (wav, mp3, ogg o aac) en
vez de una receta. Entonces el motor lo reproduce y sólo respeta su volumen — de un fichero no se
puede cambiar el tono. Si el fichero falla al cargar, suena la receta de reserva que acompaña a
toda entrada de ese tipo, así que un 404 no deja nada mudo.
< / p >
< / section >
<!-- ── 6 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 6 · Míralo funcionar< / h2 >
< p data-uix-section-desc >
Elige familia, verbo e intent y observa las dos búsquedas y la firma que sale. Para oírlo,
enciende < em > Sound< / em > en el menú < em > Semantics< / em > de la barra superior.
< / p >
< div data-uix-controls >
@ -183,7 +332,7 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
{ #each INTENTS as i } < option value = { i } > { i } </option > { /each }
< / select >
< / label >
< button type = "button" data-uix-sound-chip onclick = { () => play () }>
< button type = "button" data-uix-sound-chip onclick = { play }>
< span data-uix-sound-chip-name > oírlo< / span >
< span data-uix-sound-chip-play > ▶< / span >
< / button >
@ -195,7 +344,7 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
< tr >
< th scope = "row" >
< span data-uix-sound-layer > Paso 1 — el nombre< / span >
< span data-uix-sound-layer-note > lo dice l a tabla de la familia< / span >
< span data-uix-sound-layer-note > la tabla de la familia, afinada por verbo < / span >
< / th >
< td > { chosenName ?? 'esta familia no suena' } </ td >
< / tr >
@ -208,18 +357,22 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
{ #if ! chosenEntry }
no suena nada
{ :else if chosenEntry . includes ( '.' )}
{ chosenEntry } — sí, hay versión propia
{ chosenEntry } — sí, versión propia
{ : else }
{ chosenEntry } — no hay versión, se ignora el intent
{ /if }
< / td >
< / tr >
< tr >
< th scope = "row" > < span data-uix-sound-layer > Lo que se oye< / span > < / th >
< th scope = "row" >
< span data-uix-sound-layer > La firma< / span >
< span data-uix-sound-layer-note > lo que recibe el motor de audio< / span >
< / th >
< td >
{ #if resolved }
{ resolved . pitch } Hz · { resolved . duration } ms · { resolved . contour }
{ #if resolved . roughness >= 0.2 } · áspero{ /if }
{ resolved . pitch } Hz · brillo { resolved . centroid } · { resolved . duration } ms ·
{ resolved . contour }
{ #if resolved . roughness >= 0.2 } · áspero ({ resolved . roughness } ){ /if }
{ : else }
silencio
{ /if }
@ -230,19 +383,61 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
< / div >
< p data-uix-section-desc >
Tres combinaciones que enseñan la regla entera: < code > commit< / code > +
< code > threat< / code > (hay versión, suena otra cosa) · < code > commit< / code > +
< code > affirm< / code > (no hay versión, se ignora el intent) · < code > sustain< / code >
(esa familia no suena, y está bien).
Tres casos que enseñan la regla entera: < code > commit< / code > + < code > threat< / code >
(hay versión, suena otra cosa) · < code > commit< / code > + < code > affirm< / code > (no hay versión, se
ignora el intent) · < code > sustain< / code > (esa familia no suena, y está bien así).
< / p >
< / section >
<!-- ── 7 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 7 · Lo que decide si llega a oírse< / h2 >
< p data-uix-section-desc >
Elegir un sonido no significa reproducirlo. Entre la firma y el altavoz hay varias políticas,
y todas existen por el objetivo de no fatigar:
< / p >
< ul data-uix-prose-list >
< li >
< strong > Preferencias.< / strong > El canal admite tres niveles: < code > full< / code > ,
< code > reduce< / code > (multiplica el volumen por 0,4) y < code > off< / code > (no suena nada, y el
motor de audio ni se abre). Lo controla la aplicación — en esta documentación, el
interruptor < em > Sound< / em > de la barra superior.
< / li >
< li >
< strong > Memoria de frecuencia.< / strong > A partir de la tercera repetición del mismo suceso
en dos segundos, el volumen baja un 15 % cada vez, hasta un suelo del 25 %. Una pausa lo
reinicia. Los sucesos de < code > threat< / code > están exentos —una alarma no se cansa— y los
gestos continuos también, porque en ellos la repetición
< em > es< / em > el mensaje.
< / li >
< li >
< strong > Silencio declarado.< / strong > Un componente puede decir < code > SILENT< / code >
en lugar de un nombre. No es «volumen cero»: el canal se retira, no se abre contexto de audio,
no se calcula nada. Es una decisión de diseño explícita, y varios componentes muy frecuentes la
usan.
< / li >
< li >
< strong > Buses.< / strong > Los sonidos de interfaz van por un bus separado del audio de contenido
(un vídeo, un reproductor). Bajar el volumen de la interfaz no toca el trabajo del usuario, y
cuando suena contenido la interfaz se aparta sola.
< / li >
< li >
< strong > Ventana perceptual.< / strong > Cada suceso tiene una duración durante la que se
considera «vivo» — una escala con nombres (< code > glimpse< / code > 120 ms,
< code > brief< / code > 240, < code > settled< / code > 400, < code > noticed< / code > 600…). Es lo que mantiene
los atributos puestos para que el CSS reaccione, y coordina el sonido con lo que se ve.
< / li >
< / ul >
< / section >
<!-- ── 8 ─────────────────────────────────────────────────────────────── -->
< section data-uix-section >
< h2 data-uix-section-title > 4 · Cuando un componente quiere otra cosa< / h2 >
< h2 data-uix-section-title > 8 · Cuando un componente quiere otra cosa< / h2 >
< p data-uix-section-desc >
A veces un componente necesita apartarse del sonido por defecto de su familia. Lo hace < strong
>nombrando otro< /strong
>, en una línea:
>, en su fichero de sema (< code > src/uix/sema/components/{ nombre} .ts< / code > ), con una
línea:
< / p >
< pre data-uix-code > < code
>{ `// El botón de este componente no hace 'touch', hace 'snap'
@ -250,26 +445,27 @@ sonido = catálogo[nombre.intent] ?? catálogo[nombre] ?? nada`}</code
>< / pre >
< p data-uix-section-desc >
Lo que < strong > nunca< / strong > hace —ni él, ni un tema, ni la aplicación— es escribir un
parámetro. No existe «súbele el tono 200 Hz». E l tipo acepta un nombre del catálogo o
< code > SILENT< / code > , y nada más. Si hace falta un sonido que no está, se
parámetro. No existe «súbele el tono 200 Hz»: e l tipo acepta un nombre del catálogo o
< code > SILENT< / code > , y el compilador rechaza lo de más. Si hace falta un sonido que no está, se
< a href = "/uix/docs/sound/packs" > añade al catálogo< / a > con su nombre y luego se nombra.
< / p >
< p data-uix-section-desc >
Por eso la mayoría de componentes del framework no tienen ninguna regla de sonido: el sistema
ya dice lo correcto, y sólo se escribe una línea cuando hay una decisión de verdad que tomar.
Por eso la mayoría de componentes no tienen ninguna regla de sonido: el sistema ya dice lo
correcto por familia y verbo, y sólo se escribe una línea cuando hay una decisión real que
tomar.
< / p >
< / section >
< section data-uix-section >
< h2 data-uix-section-title > Y a partir de aquí< / h2 >
< h2 data-uix-section-title > Seguir por aquí< / h2 >
< ul data-uix-prose-list >
< li >
< a href = "/uix/docs/sound/catalogo" > < strong > El catálogo< / strong > < / a > — cada sonido que existe,
por qué suena así, y audible .
audible, y por qué suena así.
< / li >
< li >
< a href = "/uix/docs/sound/packs" > < strong > Sound packs< / strong > < / a > — traer tu propia voz:
síntesis, < code > .wav< / code > , < code > .mp3< / code > , y cómo registrar nombres nuevos.
síntesis, < code > .wav< / code > , < code > .mp3< / code > , y registrar nombres nuevos.
< / li >
< li >
< a href = "/uix/docs/sound/gestos" > < strong > Gestos< / strong > < / a > — por qué un arrastre suena por repetición