claude-code-arcane 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +6 -4
- package/agents/audio/audio-director.md +90 -0
- package/agents/audio/audio-programmer.md +97 -0
- package/agents/audio/audio-qa.md +95 -0
- package/agents/audio/composer.md +88 -0
- package/agents/audio/technical-sound-designer.md +90 -0
- package/agents/audio/voice-director.md +96 -0
- package/docs/SKILLS-CATALOG.md +45 -2
- package/docs/USER-GUIDE.md +2 -2
- package/package.json +1 -1
- package/profiles/audio.yaml +45 -0
- package/profiles/second-brain.yaml +37 -0
- package/rules/gamedev/audio-code.md +45 -0
- package/rules/vault-conventions.md +144 -0
- package/skills/adaptive-music/SKILL.md +171 -0
- package/skills/adaptive-music/references/anti-patterns.md +53 -0
- package/skills/audio-audit/SKILL.md +164 -0
- package/skills/audio-audit/references/anti-patterns.md +65 -0
- package/skills/audio-audit/scripts/check_assets.py +208 -0
- package/skills/audio-bible/SKILL.md +119 -0
- package/skills/audio-bible/references/anti-patterns.md +39 -0
- package/skills/audio-mix/SKILL.md +151 -0
- package/skills/audio-mix/references/anti-patterns.md +65 -0
- package/skills/audio-spec/SKILL.md +107 -0
- package/skills/audio-spec/references/anti-patterns.md +45 -0
- package/skills/brain-dump/SKILL.md +53 -0
- package/skills/hub-note/SKILL.md +68 -0
- package/skills/middleware-integration/SKILL.md +186 -0
- package/skills/middleware-integration/references/anti-patterns.md +69 -0
- package/skills/midi-compose/SKILL.md +136 -0
- package/skills/midi-compose/references/smf-format.md +129 -0
- package/skills/midi-compose/scripts/compose_midi.py +326 -0
- package/skills/midi-compose/templates/example-composition.json +69 -0
- package/skills/music-composition/SKILL.md +106 -0
- package/skills/music-composition/references/form.md +57 -0
- package/skills/music-composition/references/game-music.md +82 -0
- package/skills/music-composition/references/harmony.md +82 -0
- package/skills/music-composition/references/melody.md +48 -0
- package/skills/music-composition/references/orchestration.md +69 -0
- package/skills/music-composition/references/rhythm-groove.md +49 -0
- package/skills/obsidian-bases/SKILL.md +85 -0
- package/skills/obsidian-bases/references/bases-syntax.md +123 -0
- package/skills/obsidian-canvas/SKILL.md +91 -0
- package/skills/obsidian-markdown/SKILL.md +110 -0
- package/skills/pdf-generator/scripts/fill_inspect.py +2 -0
- package/skills/pdf-generator/scripts/render_body.py +2 -0
- package/skills/procedural-audio/SKILL.md +157 -0
- package/skills/procedural-audio/references/anti-patterns.md +45 -0
- package/skills/review-dump/SKILL.md +108 -0
- package/skills/review-monthly/SKILL.md +56 -0
- package/skills/review-weekly/SKILL.md +91 -0
- package/skills/second-brain/SKILL.md +211 -0
- package/skills/second-brain/hooks/hooks.json +16 -0
- package/skills/second-brain/hooks/validate-note.sh +85 -0
- package/skills/second-brain/references/cross-project.md +57 -0
- package/skills/second-brain/references/templates/Atomic.md +22 -0
- package/skills/second-brain/references/templates/Daily.md +25 -0
- package/skills/second-brain/references/templates/Dashboard.base +70 -0
- package/skills/second-brain/references/templates/Dump.md +11 -0
- package/skills/second-brain/references/templates/Hub.md +22 -0
- package/skills/second-brain/references/templates/Monthly.md +29 -0
- package/skills/second-brain/references/templates/Weekly.md +29 -0
- package/skills/second-brain/references/templates/codebases.md +37 -0
- package/skills/second-brain/references/templates/hot.md +37 -0
- package/skills/second-brain/references/templates/vault-CLAUDE.md +127 -0
- package/skills/sfx-design/SKILL.md +130 -0
- package/skills/sfx-design/references/anti-patterns.md +53 -0
- package/skills/spatial-audio/SKILL.md +139 -0
- package/skills/spatial-audio/references/anti-patterns.md +53 -0
- package/skills/ui-sound-design/SKILL.md +133 -0
- package/skills/ui-sound-design/references/anti-patterns.md +57 -0
- package/skills/vault-audit/SKILL.md +72 -0
- package/skills/vault-audit/scripts/vault_audit.py +539 -0
- package/skills/vault-clip/SKILL.md +73 -0
- package/skills/vault-recall/SKILL.md +82 -0
- package/skills/vault-recall/scripts/vault_index.py +626 -0
- package/skills/vault-tidy/SKILL.md +76 -0
- package/skills/voice-pipeline/SKILL.md +150 -0
- package/skills/voice-pipeline/references/anti-patterns.md +69 -0
- package/skills/xlsx-generator/scripts/formula_check.py +2 -0
- package/skills/xlsx-generator/scripts/libreoffice_recalc.py +2 -0
- package/skills/xlsx-generator/scripts/xlsx_add_column.py +2 -0
- package/skills/xlsx-generator/scripts/xlsx_insert_row.py +2 -0
- package/skills/xlsx-generator/scripts/xlsx_reader.py +2 -0
- package/skills/zettel/SKILL.md +66 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-audit
|
|
3
|
+
description: "Auditoria de audio: voice count, CPU, memoria, streaming, compliance de loudness (ASWG-R001), conformidad de assets contra spec y checklist por plataforma. Usar para: QA de audio, performance de audio, verificar loudness, revisar assets, pre-certificacion."
|
|
4
|
+
category: "audio"
|
|
5
|
+
argument-hint: "[full | perf | loudness | assets | platform]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Write, Edit, Bash
|
|
8
|
+
---
|
|
9
|
+
# audio-audit — QA y Compliance de Audio
|
|
10
|
+
|
|
11
|
+
Verifica que el audio del proyecto cumple: performance dentro del presupuesto, assets conformes al spec, y loudness dentro del target de plataforma.
|
|
12
|
+
|
|
13
|
+
## Cuándo usar
|
|
14
|
+
|
|
15
|
+
- Antes de una milestone o de certificación
|
|
16
|
+
- El audio se corta, se satura o come demasiado CPU
|
|
17
|
+
- Verificar assets entregados por un contractor
|
|
18
|
+
- Auditar loudness para cumplir requisitos de plataforma
|
|
19
|
+
- Después de integrar un sistema nuevo, para medir su costo real
|
|
20
|
+
|
|
21
|
+
## Input
|
|
22
|
+
|
|
23
|
+
1. Leer audio bible (sección 8, standards) y los specs de `/audio-spec`
|
|
24
|
+
2. Determinar plataformas target, y cuál es la más baja
|
|
25
|
+
3. Preguntar al usuario: alcance (`full` / solo perf / solo loudness / solo assets) y si hay captura de profiler disponible
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Performance
|
|
30
|
+
|
|
31
|
+
**Presupuesto:** el audio dispone del **5-10% del frame time**. A 60 fps el frame son 16.6 ms, así que el audio tiene ~0.8-1.7 ms. A 90 fps (VR) el frame son ~11 ms y el margen es menor.
|
|
32
|
+
|
|
33
|
+
| Métrica | Cómo se mide | Verdict |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| **CPU de audio** | Profiler del middleware, en gameplay real | FAIL si supera el 10% del frame sostenido |
|
|
36
|
+
| **Voces activas** | Pico vs límite configurado | CONCERNS si el pico toca el límite; FAIL si hay descartes de sonidos de prioridad alta |
|
|
37
|
+
| **Memoria de audio** | Por bank y total, vs budget del proyecto | FAIL si supera el budget en la plataforma más baja |
|
|
38
|
+
| **Accesos de streaming** | Contador de streaming del profiler | CONCERNS si hay starvation |
|
|
39
|
+
| **Game objects registrados** | Contador, medido a lo largo de una sesión larga | FAIL si crece monótonamente (leak) |
|
|
40
|
+
|
|
41
|
+
**Medir en gameplay real, no en el editor.** El editor dispara eventos en orden, uno por vez, sin ruido. El jugador produce combinaciones que nadie planificó. Y medir en la **plataforma más baja** del target: el presupuesto que cierra en PC no dice nada de un handheld.
|
|
42
|
+
|
|
43
|
+
**El leak de game objects es el hallazgo más frecuente.** Si el contador de objetos registrados crece durante una sesión de dos horas y nunca baja, hay des-registros faltantes. Se ve solo en sesiones largas.
|
|
44
|
+
|
|
45
|
+
### Costos de referencia
|
|
46
|
+
|
|
47
|
+
Para saber si un número medido es razonable:
|
|
48
|
+
|
|
49
|
+
| Operación | Costo esperado por fuente |
|
|
50
|
+
|---|---|
|
|
51
|
+
| Convolución HRTF (512 taps) | ~2 ms |
|
|
52
|
+
| Encode ambisónico | ~0.1 ms |
|
|
53
|
+
| Decode ambisónico a binaural | ~1 ms total |
|
|
54
|
+
| Footstep procedural | ~1-2 ms |
|
|
55
|
+
| Síntesis de viento | ~0.5 ms/frame |
|
|
56
|
+
| Panning + atenuación | despreciable |
|
|
57
|
+
|
|
58
|
+
Si lo medido se desvía mucho del esperado, la causa suele ser configuración (más taps, más fuentes espacializadas de las previstas) antes que el motor.
|
|
59
|
+
|
|
60
|
+
## 2. Loudness
|
|
61
|
+
|
|
62
|
+
Referencia de industria en juegos: **ASWG-R001** (Sony), la única especificación publicada por una plataforma mayor.
|
|
63
|
+
|
|
64
|
+
| Contexto | Loudness promedio | True peak máximo |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| Consola / home | **−23 LUFS** (±2) | **−1 dBTP** |
|
|
67
|
+
| Portable / handheld | **−18 LUFS** | −1 dBTP |
|
|
68
|
+
|
|
69
|
+
**Cómo medir bien:**
|
|
70
|
+
|
|
71
|
+
- Se mide el **programa completo en gameplay representativo**, no un asset aislado. Medir un `.wav` no dice nada sobre cumplimiento
|
|
72
|
+
- Sesión de duración suficiente para promediar: al menos varios minutos cubriendo exploración, combate y diálogo
|
|
73
|
+
- Medir el **true peak**, no el peak de sample: la codificación lossy puede generar picos por encima del valor medido en PCM
|
|
74
|
+
- **Confirmar el target vigente** de cada plataforma antes de certificación. Las recomendaciones se revisan — el promedio de ASWG también se citó como −24 LKFS, así que no confiar en un número memorizado
|
|
75
|
+
|
|
76
|
+
**Verdict:** COMPLIANT dentro del target con su tolerancia; NON-COMPLIANT fuera, o con true peak por encima de −1 dBTP.
|
|
77
|
+
|
|
78
|
+
## 3. Conformidad de assets
|
|
79
|
+
|
|
80
|
+
Verificación mecánica contra los standards del bible. Esto se automatiza:
|
|
81
|
+
|
|
82
|
+
| Chequeo | Falla típica |
|
|
83
|
+
|---|---|
|
|
84
|
+
| Sample rate | Mezcla de 44.1 y 48 kHz en el mismo proyecto |
|
|
85
|
+
| Bit depth | 24-bit donde el pipeline espera 16 |
|
|
86
|
+
| Canales | Estéreo en un asset que se usa como fuente 3D mono |
|
|
87
|
+
| Naming | No sigue el patrón; rompe batch y automatización de banks |
|
|
88
|
+
| DC offset | Produce clicks al concatenar |
|
|
89
|
+
| Silencio al inicio | Retrasa el ataque; se percibe como lag |
|
|
90
|
+
| Clipping | Picos a 0 dBFS en el asset crudo |
|
|
91
|
+
| Duración vs spec | Fuera del rango especificado |
|
|
92
|
+
| Loudness por categoría | Assets de la misma categoría con niveles dispares |
|
|
93
|
+
|
|
94
|
+
**Fuente 3D en estéreo es el hallazgo más común y el más silencioso:** el middleware la trata distinto o la colapsa, y la localización espacial se degrada sin que nadie vea un error.
|
|
95
|
+
|
|
96
|
+
La skill trae el verificador: `scripts/check_assets.py`, stdlib-only (módulo `wave`), recursivo sobre una carpeta.
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
python skills/audio-audit/scripts/check_assets.py assets/audio \
|
|
100
|
+
--rate 48000 --bits 16 --mono-3d --naming '^(sfx|vo|mus)_'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Chequea sample rate, bit depth, canales, naming, clipping, DC offset, silencio inicial, archivos vacíos o silenciosos, y avisa cuando el sample rate obliga a un resampleo costoso. Emite `[FAIL]` / `[WARN]` / `[SKIP]` por hallazgo y cierra con COMPLIANT / CONCERNS / NON-COMPLIANT; exit code 1 solo si hay FAIL, para usarlo en CI.
|
|
104
|
+
|
|
105
|
+
Lo que necesita una herramienta externa (loudness integrado, análisis espectral) se reporta como `[SKIP]` en vez de abortar la corrida.
|
|
106
|
+
|
|
107
|
+
## 4. Checklist por plataforma
|
|
108
|
+
|
|
109
|
+
| Ítem | Aplica a |
|
|
110
|
+
|---|---|
|
|
111
|
+
| Loudness dentro del target del contexto | Todas |
|
|
112
|
+
| True peak ≤ −1 dBTP | Todas |
|
|
113
|
+
| Manejo de interrupciones (llamada, alarma) | Móvil |
|
|
114
|
+
| Manejo de cambio de ruta (auriculares) | Móvil, consola |
|
|
115
|
+
| Respeto del switch de silencio | iOS |
|
|
116
|
+
| Comportamiento en background | Móvil |
|
|
117
|
+
| Mezcla con audio de terceros | Móvil |
|
|
118
|
+
| Audio no rompe al perder foco | Todas |
|
|
119
|
+
| Memoria dentro del budget de la plataforma | Todas |
|
|
120
|
+
| Se puede mutear todo desde opciones | Todas |
|
|
121
|
+
| Controles separados: música / SFX / voz / UI | Todas |
|
|
122
|
+
| Información crítica no exclusivamente por audio | Todas (accesibilidad) |
|
|
123
|
+
| Subtítulos para todo el diálogo | Todas (accesibilidad) |
|
|
124
|
+
|
|
125
|
+
El comportamiento en background y el switch de silencio en iOS son causas concretas de rechazo de store, no recomendaciones.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Proceso
|
|
130
|
+
|
|
131
|
+
1. **Determinar el alcance** y la plataforma más baja del target
|
|
132
|
+
2. **Capturar** con el profiler del middleware en gameplay representativo, no en el editor
|
|
133
|
+
3. **Medir performance** contra el presupuesto y comparar con los costos de referencia
|
|
134
|
+
4. **Medir loudness** del programa completo, con true peak
|
|
135
|
+
5. **Verificar assets** contra los standards, automatizado si el volumen lo justifica
|
|
136
|
+
6. **Recorrer el checklist** de plataforma
|
|
137
|
+
7. **Reportar** con severidad por hallazgo y, si el usuario lo pide, preguntar "¿Escribo el reporte a `<path>`?" antes de usar Write
|
|
138
|
+
|
|
139
|
+
### Formato del reporte
|
|
140
|
+
|
|
141
|
+
Por hallazgo: qué se midió, valor medido, valor esperado, severidad, y dónde está el problema. Un hallazgo sin número medido no es un hallazgo, es una impresión.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Verdict
|
|
146
|
+
|
|
147
|
+
Global, y por sección:
|
|
148
|
+
|
|
149
|
+
- **COMPLIANT** — performance dentro del presupuesto en la plataforma más baja, loudness dentro del target, assets conformes, checklist completo
|
|
150
|
+
- **CONCERNS** — hay desvíos que no bloquean: pico de voces tocando el límite, assets con naming inconsistente, loudness en el borde de la tolerancia
|
|
151
|
+
- **NON-COMPLIANT** — CPU o memoria fuera de presupuesto, loudness fuera del target, leak de game objects, o un ítem de checklist que es causa de rechazo de plataforma
|
|
152
|
+
|
|
153
|
+
Un NON-COMPLIANT en cualquier sección bloquea certificación.
|
|
154
|
+
|
|
155
|
+
## Anti-patterns
|
|
156
|
+
|
|
157
|
+
> → Read references/anti-patterns.md
|
|
158
|
+
|
|
159
|
+
## Next steps
|
|
160
|
+
|
|
161
|
+
- `/spatial-audio` si el costo de espacialización está fuera de presupuesto
|
|
162
|
+
- `/audio-mix` si el problema es loudness o balance entre categorías
|
|
163
|
+
- `/middleware-integration` si hay leaks, banks mal organizados o descartes de voces
|
|
164
|
+
- `/procedural-audio` si la memoria no cierra por cantidad de samples
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Audio Audit — Anti-patterns
|
|
2
|
+
|
|
3
|
+
## Medir en el editor
|
|
4
|
+
|
|
5
|
+
El editor dispara eventos en orden, uno por vez, sin ruido de fondo. El jugador produce combinaciones que nadie planificó. Las mediciones del editor no predicen nada.
|
|
6
|
+
|
|
7
|
+
## Medir en la plataforma más alta
|
|
8
|
+
|
|
9
|
+
El presupuesto cierra cómodo en PC y revienta en el handheld. La auditoría se hace contra el piso del target.
|
|
10
|
+
|
|
11
|
+
## Medir loudness de un asset
|
|
12
|
+
|
|
13
|
+
Los targets de LUFS son del programa completo en gameplay representativo. Medir un `.wav` no dice nada sobre cumplimiento.
|
|
14
|
+
|
|
15
|
+
## Sesión de medición demasiado corta
|
|
16
|
+
|
|
17
|
+
Treinta segundos de exploración tranquila da un LUFS que no representa el juego. La medición tiene que cubrir exploración, combate y diálogo.
|
|
18
|
+
|
|
19
|
+
## Confiar en un target memorizado
|
|
20
|
+
|
|
21
|
+
Las recomendaciones de plataforma se revisan. Confirmar el vigente antes de certificación.
|
|
22
|
+
|
|
23
|
+
## Medir peak de sample en vez de true peak
|
|
24
|
+
|
|
25
|
+
Un mix que llega a 0 dBFS en PCM puede generar picos por encima al codificarse en lossy. El −1 dBTP existe justamente por eso.
|
|
26
|
+
|
|
27
|
+
## No buscar leaks
|
|
28
|
+
|
|
29
|
+
El contador de game objects registrados que crece durante dos horas y nunca baja es el hallazgo más frecuente, y solo se ve en sesiones largas. Una prueba de cinco minutos no lo encuentra.
|
|
30
|
+
|
|
31
|
+
## Aceptar el pico de voces al límite
|
|
32
|
+
|
|
33
|
+
Si el pico toca el límite configurado, cualquier situación un poco peor empieza a descartar sonidos. Y si lo que se descarta es feedback de la acción del jugador, el juego se siente roto sin ningún error visible.
|
|
34
|
+
|
|
35
|
+
## Ignorar qué se descarta
|
|
36
|
+
|
|
37
|
+
El número de descartes por sí solo no dice nada. Lo que importa es la prioridad de lo descartado: perder un ambiente lejano está bien, perder el disparo del jugador no.
|
|
38
|
+
|
|
39
|
+
## Fuente 3D en estéreo
|
|
40
|
+
|
|
41
|
+
El hallazgo más silencioso: el middleware la colapsa o la trata distinto, y la localización espacial se degrada sin que aparezca ningún error. Verificar canales por categoría de uso.
|
|
42
|
+
|
|
43
|
+
## Mezcla de sample rates
|
|
44
|
+
|
|
45
|
+
44.1 y 48 kHz en el mismo proyecto obliga a resampleo en runtime, que cuesta CPU y puede introducir artefactos. Un solo sample rate por proyecto.
|
|
46
|
+
|
|
47
|
+
## Silencio al inicio del asset
|
|
48
|
+
|
|
49
|
+
Retrasa el ataque y se percibe como lag de input, aunque el código responda instantáneo. Se detecta automáticamente y casi nunca se busca.
|
|
50
|
+
|
|
51
|
+
## Hallazgo sin número
|
|
52
|
+
|
|
53
|
+
"El audio suena saturado" no es un hallazgo. Qué se midió, valor medido, valor esperado, dónde. Sin eso, nadie puede arreglarlo ni verificar que se arregló.
|
|
54
|
+
|
|
55
|
+
## Auditar a mano miles de assets
|
|
56
|
+
|
|
57
|
+
La conformidad de sample rate, canales, naming y duración es mecánica: se automatiza. La inspección manual de 4.000 archivos garantiza que algo se pase por alto.
|
|
58
|
+
|
|
59
|
+
## Fallar cuando falta una herramienta externa
|
|
60
|
+
|
|
61
|
+
Si el chequeo profundo necesita una herramienta que no está instalada, reportar `[SKIP]` y seguir. Abortar la auditoría entera por una dependencia opcional deja al usuario sin los chequeos que sí se podían correr.
|
|
62
|
+
|
|
63
|
+
## Dejar el checklist de plataforma para el final
|
|
64
|
+
|
|
65
|
+
El comportamiento en background y el switch de silencio en iOS son causas concretas de rechazo. Descubrirlos en certificación cuesta una release.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Verifica conformidad mecanica de assets WAV contra los standards del proyecto.
|
|
3
|
+
|
|
4
|
+
Solo stdlib: usa el modulo `wave` para leer cabeceras y frames. Los chequeos que
|
|
5
|
+
necesitarian una herramienta externa se reportan como [SKIP], no rompen la corrida.
|
|
6
|
+
|
|
7
|
+
Uso:
|
|
8
|
+
python check_assets.py assets/audio
|
|
9
|
+
python check_assets.py assets/audio --rate 48000 --bits 16
|
|
10
|
+
python check_assets.py assets/audio --mono-3d --naming "^(sfx|vo|mus)_"
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
import struct
|
|
19
|
+
import sys
|
|
20
|
+
import wave
|
|
21
|
+
|
|
22
|
+
# Umbrales de deteccion. Explicitos para que se puedan discutir.
|
|
23
|
+
LEADING_SILENCE_MS = 10.0 # silencio inicial que ya se percibe como lag
|
|
24
|
+
SILENCE_FLOOR = 0.002 # ~-54 dBFS: por debajo se considera silencio
|
|
25
|
+
DC_OFFSET_LIMIT = 0.01 # 1% de fondo de escala
|
|
26
|
+
CLIP_SAMPLE_LIMIT = 3 # muestras en fondo de escala para llamarlo clipping
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Finding:
|
|
30
|
+
__slots__ = ("path", "severity", "check", "detail")
|
|
31
|
+
|
|
32
|
+
def __init__(self, path: str, severity: str, check: str, detail: str):
|
|
33
|
+
self.path = path
|
|
34
|
+
self.severity = severity # FAIL | WARN | SKIP
|
|
35
|
+
self.check = check
|
|
36
|
+
self.detail = detail
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def read_samples(handle: wave.Wave_read, limit: int | None = None) -> list[int]:
|
|
40
|
+
"""Devuelve las muestras del primer canal, normalizadas a int con signo."""
|
|
41
|
+
width = handle.getsampwidth()
|
|
42
|
+
channels = handle.getnchannels()
|
|
43
|
+
frames = handle.getnframes() if limit is None else min(limit, handle.getnframes())
|
|
44
|
+
raw = handle.readframes(frames)
|
|
45
|
+
|
|
46
|
+
if width == 1:
|
|
47
|
+
# 8-bit WAV es unsigned; centrar en 0
|
|
48
|
+
values = [byte - 128 for byte in raw]
|
|
49
|
+
elif width == 2:
|
|
50
|
+
values = list(struct.unpack(f"<{len(raw) // 2}h", raw[: len(raw) // 2 * 2]))
|
|
51
|
+
elif width == 3:
|
|
52
|
+
values = []
|
|
53
|
+
for offset in range(0, len(raw) - 2, 3):
|
|
54
|
+
chunk = raw[offset:offset + 3]
|
|
55
|
+
value = int.from_bytes(chunk, "little", signed=True)
|
|
56
|
+
values.append(value)
|
|
57
|
+
elif width == 4:
|
|
58
|
+
values = list(struct.unpack(f"<{len(raw) // 4}i", raw[: len(raw) // 4 * 4]))
|
|
59
|
+
else:
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
return values[::channels] if channels > 1 else values
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def full_scale(width: int) -> float:
|
|
66
|
+
return float(2 ** (8 * width - 1))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def check_file(path: str, args: argparse.Namespace) -> list[Finding]:
|
|
70
|
+
findings: list[Finding] = []
|
|
71
|
+
name = os.path.basename(path)
|
|
72
|
+
|
|
73
|
+
if args.naming and not re.search(args.naming, name):
|
|
74
|
+
findings.append(Finding(path, "FAIL", "naming",
|
|
75
|
+
f"no matchea {args.naming!r}"))
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
with wave.open(path, "rb") as handle:
|
|
79
|
+
rate = handle.getframerate()
|
|
80
|
+
width = handle.getsampwidth()
|
|
81
|
+
channels = handle.getnchannels()
|
|
82
|
+
frames = handle.getnframes()
|
|
83
|
+
duration = frames / rate if rate else 0.0
|
|
84
|
+
|
|
85
|
+
if args.rate and rate != args.rate:
|
|
86
|
+
findings.append(Finding(path, "FAIL", "sample-rate",
|
|
87
|
+
f"{rate} Hz, se esperaba {args.rate} Hz"))
|
|
88
|
+
if args.bits and width * 8 != args.bits:
|
|
89
|
+
findings.append(Finding(path, "FAIL", "bit-depth",
|
|
90
|
+
f"{width * 8}-bit, se esperaba {args.bits}-bit"))
|
|
91
|
+
if args.mono_3d and channels != 1:
|
|
92
|
+
findings.append(Finding(
|
|
93
|
+
path, "FAIL", "channels",
|
|
94
|
+
f"{channels} canales; una fuente 3D tiene que ser mono o el "
|
|
95
|
+
f"middleware la colapsa y se degrada la localizacion"))
|
|
96
|
+
if args.rate and rate and rate != args.rate and args.rate % rate and rate % args.rate:
|
|
97
|
+
findings.append(Finding(path, "WARN", "resample",
|
|
98
|
+
f"{rate} Hz no es multiplo ni divisor de "
|
|
99
|
+
f"{args.rate} Hz: el resampleo es mas costoso"))
|
|
100
|
+
if frames == 0:
|
|
101
|
+
findings.append(Finding(path, "FAIL", "empty", "0 frames"))
|
|
102
|
+
return findings
|
|
103
|
+
if args.max_duration and duration > args.max_duration:
|
|
104
|
+
findings.append(Finding(path, "WARN", "duration",
|
|
105
|
+
f"{duration:.2f} s > {args.max_duration:.2f} s"))
|
|
106
|
+
|
|
107
|
+
samples = read_samples(handle)
|
|
108
|
+
if not samples:
|
|
109
|
+
findings.append(Finding(path, "SKIP", "content",
|
|
110
|
+
f"{width * 8}-bit no soportado por el lector stdlib"))
|
|
111
|
+
return findings
|
|
112
|
+
|
|
113
|
+
scale = full_scale(width)
|
|
114
|
+
peak = max(abs(value) for value in samples)
|
|
115
|
+
|
|
116
|
+
clipped = sum(1 for value in samples if abs(value) >= scale - 1)
|
|
117
|
+
if clipped >= CLIP_SAMPLE_LIMIT:
|
|
118
|
+
findings.append(Finding(path, "FAIL", "clipping",
|
|
119
|
+
f"{clipped} muestras en fondo de escala"))
|
|
120
|
+
|
|
121
|
+
offset = sum(samples) / len(samples) / scale
|
|
122
|
+
if abs(offset) > DC_OFFSET_LIMIT:
|
|
123
|
+
findings.append(Finding(path, "WARN", "dc-offset",
|
|
124
|
+
f"{offset * 100:+.2f}% - produce clicks al concatenar"))
|
|
125
|
+
|
|
126
|
+
floor = SILENCE_FLOOR * scale
|
|
127
|
+
if peak < floor:
|
|
128
|
+
# Archivo enteramente silencioso: el chequeo de silencio inicial
|
|
129
|
+
# sobra, seria el mismo hallazgo dos veces.
|
|
130
|
+
findings.append(Finding(path, "FAIL", "silent", "el archivo es silencio"))
|
|
131
|
+
return findings
|
|
132
|
+
|
|
133
|
+
leading = 0
|
|
134
|
+
for value in samples:
|
|
135
|
+
if abs(value) > floor:
|
|
136
|
+
break
|
|
137
|
+
leading += 1
|
|
138
|
+
leading_ms = leading / rate * 1000
|
|
139
|
+
if leading_ms > LEADING_SILENCE_MS:
|
|
140
|
+
findings.append(Finding(
|
|
141
|
+
path, "WARN", "leading-silence",
|
|
142
|
+
f"{leading_ms:.1f} ms de silencio inicial - se percibe como lag de input"))
|
|
143
|
+
|
|
144
|
+
except wave.Error as error:
|
|
145
|
+
findings.append(Finding(path, "SKIP", "format",
|
|
146
|
+
f"no es un WAV PCM legible ({error})"))
|
|
147
|
+
except OSError as error:
|
|
148
|
+
findings.append(Finding(path, "FAIL", "io", str(error)))
|
|
149
|
+
|
|
150
|
+
return findings
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main(argv: list[str] | None = None) -> int:
|
|
154
|
+
parser = argparse.ArgumentParser(
|
|
155
|
+
description="Verifica conformidad de assets WAV contra los standards del proyecto.")
|
|
156
|
+
parser.add_argument("root", help="carpeta a auditar (recursivo)")
|
|
157
|
+
parser.add_argument("--rate", type=int, help="sample rate esperado, ej 48000")
|
|
158
|
+
parser.add_argument("--bits", type=int, choices=(8, 16, 24, 32),
|
|
159
|
+
help="bit depth esperado")
|
|
160
|
+
parser.add_argument("--mono-3d", action="store_true",
|
|
161
|
+
help="exigir mono (assets usados como fuentes 3D)")
|
|
162
|
+
parser.add_argument("--naming", help="regex que el nombre de archivo debe matchear")
|
|
163
|
+
parser.add_argument("--max-duration", type=float,
|
|
164
|
+
help="duracion maxima en segundos")
|
|
165
|
+
args = parser.parse_args(argv)
|
|
166
|
+
|
|
167
|
+
if not os.path.isdir(args.root):
|
|
168
|
+
print(f"[FAIL] no es una carpeta: {args.root}", file=sys.stderr)
|
|
169
|
+
return 1
|
|
170
|
+
|
|
171
|
+
paths = []
|
|
172
|
+
for directory, _subdirs, files in os.walk(args.root):
|
|
173
|
+
for name in sorted(files):
|
|
174
|
+
if name.lower().endswith(".wav"):
|
|
175
|
+
paths.append(os.path.join(directory, name))
|
|
176
|
+
|
|
177
|
+
if not paths:
|
|
178
|
+
print(f"[SKIP] no hay .wav en {args.root}")
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
findings: list[Finding] = []
|
|
182
|
+
for path in paths:
|
|
183
|
+
findings.extend(check_file(path, args))
|
|
184
|
+
|
|
185
|
+
fails = [f for f in findings if f.severity == "FAIL"]
|
|
186
|
+
warns = [f for f in findings if f.severity == "WARN"]
|
|
187
|
+
skips = [f for f in findings if f.severity == "SKIP"]
|
|
188
|
+
|
|
189
|
+
for group in (fails, warns, skips):
|
|
190
|
+
for finding in group:
|
|
191
|
+
relative = os.path.relpath(finding.path, args.root)
|
|
192
|
+
print(f"[{finding.severity}] {relative}: {finding.check} - {finding.detail}")
|
|
193
|
+
|
|
194
|
+
print()
|
|
195
|
+
print(f"{len(paths)} assets | {len(fails)} FAIL | {len(warns)} WARN | {len(skips)} SKIP")
|
|
196
|
+
|
|
197
|
+
if fails:
|
|
198
|
+
print("NON-COMPLIANT")
|
|
199
|
+
return 1
|
|
200
|
+
if warns:
|
|
201
|
+
print("CONCERNS")
|
|
202
|
+
return 0
|
|
203
|
+
print("COMPLIANT")
|
|
204
|
+
return 0
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
if __name__ == "__main__":
|
|
208
|
+
sys.exit(main())
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audio-bible
|
|
3
|
+
description: "Audio Bible authoring: sonic palette, emotional targets per game state, frequency allocation, mix hierarchy, audio standards. Usar para: direccion de audio, paleta sonora, identidad sonora del juego."
|
|
4
|
+
category: "audio"
|
|
5
|
+
argument-hint: "[full|core|standards]"
|
|
6
|
+
user-invocable: true
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Write, Edit
|
|
8
|
+
---
|
|
9
|
+
# audio-bible — Sonic Identity Specification
|
|
10
|
+
|
|
11
|
+
Define la identidad sonora del juego. Toda produccion de audio se gatea contra este documento — es el equivalente auditivo de `/art-bible`.
|
|
12
|
+
|
|
13
|
+
## Cuándo usar
|
|
14
|
+
|
|
15
|
+
- Después de tener game concept aprobado, antes de producir un solo sonido
|
|
16
|
+
- Cuando necesitás estandarizar el criterio sonoro entre sound designers y compositores
|
|
17
|
+
- Antes de escribir specs de SFX (`/audio-spec`) o diseñar el sistema de música (`/adaptive-music`)
|
|
18
|
+
|
|
19
|
+
## Input
|
|
20
|
+
|
|
21
|
+
1. Leer game concept (elevator pitch, pilares, core fantasy) y art bible si existe
|
|
22
|
+
2. Preguntar al usuario: scope (full bible / core secciones 1-4 / solo standards), plataformas target, y referencias sonoras (juegos, films, discos)
|
|
23
|
+
3. Si ya existe audio bible: detectar secciones completas vs vacías, trabajar solo las incompletas
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Secciones del Audio Bible
|
|
28
|
+
|
|
29
|
+
### Core (secciones 1-4) — definen el lenguaje sonoro
|
|
30
|
+
|
|
31
|
+
| # | Sección | Qué define | Criterio de calidad |
|
|
32
|
+
|---|---------|-----------|-------------------|
|
|
33
|
+
| 1 | **Sonic Identity Statement** | Regla sonora de 1 línea + 2-3 principios de soporte | Resuelve cualquier ambigüedad sonora. Cada principio ancla en un pilar del juego |
|
|
34
|
+
| 2 | **Emotional Targets por Game State** | Qué debe sentir el jugador en exploración, combate, tensión, victoria, menú | Cada estado auditivamente distinto sin mirar la pantalla |
|
|
35
|
+
| 3 | **Sonic Palette** | Fuentes y texturas: acústico vs sintético, orgánico vs procesado, época/lugar, vocabulario tímbrico | Un sonido nuevo se puede aceptar o rechazar leyendo esto |
|
|
36
|
+
| 4 | **Frequency Allocation** | Qué categoría es dueña de qué banda — evita masking antes de que exista el mix | Diálogo, música, SFX y ambiente no compiten por la misma banda |
|
|
37
|
+
|
|
38
|
+
### Production (secciones 5-8) — reglas concretas
|
|
39
|
+
|
|
40
|
+
| # | Sección | Qué define |
|
|
41
|
+
|---|---------|-----------|
|
|
42
|
+
| 5 | **Mix Hierarchy** | Prioridad entre categorías, qué duckea a qué, qué nunca se pisa |
|
|
43
|
+
| 6 | **Music Direction** | Instrumentación, escalas/modos, tempo range, rol del silencio, estrategia adaptativa |
|
|
44
|
+
| 7 | **SFX Direction** | Filosofía de impacto, realismo vs estilización, tratamiento del feedback de UI, uso de variación |
|
|
45
|
+
| 8 | **Audio Standards** | Sample rate, bit depth, formatos, naming, targets de loudness, budgets de memoria y voces por plataforma |
|
|
46
|
+
|
|
47
|
+
### Reference (sección 9)
|
|
48
|
+
|
|
49
|
+
| # | Sección | Qué define |
|
|
50
|
+
|---|---------|-----------|
|
|
51
|
+
| 9 | **References & Prohibitions** | 3-5 referencias con qué tomar y qué evitar de cada una. Prohibiciones sonoras explícitas |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Frequency allocation — el punto que más se saltea
|
|
56
|
+
|
|
57
|
+
La mayoría de los mixes de juego se rompen porque nadie asignó bandas antes de producir. Definir dueño primario por banda evita rehacer assets:
|
|
58
|
+
|
|
59
|
+
| Banda | Dueño primario | Nota |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| Sub (20-60 Hz) | Impactos, explosiones | Reservado, no lo llenes de música |
|
|
62
|
+
| Low (60-250 Hz) | Música (bajo, kick), body de impactos | Zona de masking más frecuente |
|
|
63
|
+
| Low-mid (250-500 Hz) | Ambiente, body de props | Acumula barro si no se controla |
|
|
64
|
+
| Mid (500 Hz-2 kHz) | **Diálogo** — intocable | El resto cede acá |
|
|
65
|
+
| High-mid (2-6 kHz) | Feedback de UI, transientes, definición | Fatiga rápido; usar con precisión |
|
|
66
|
+
| High (6-20 kHz) | Aire, detalle, brillo | Primero en irse en compresión lossy |
|
|
67
|
+
|
|
68
|
+
Cuando un asset nuevo pelea una banda que no le corresponde, la solución es de diseño (cambiar la fuente), no de EQ.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Proceso
|
|
73
|
+
|
|
74
|
+
Para cada sección:
|
|
75
|
+
1. **Draftar** basándose en game concept, pilares y referencias
|
|
76
|
+
2. **Presentar** al usuario para review
|
|
77
|
+
3. **Escribir** a archivo inmediatamente después de aprobación — preguntar "¿Escribo esta sección a `<path>`?" antes de usar Write
|
|
78
|
+
|
|
79
|
+
No batchear — escribir cada sección aprobada antes de pasar a la siguiente.
|
|
80
|
+
|
|
81
|
+
### Conflictos
|
|
82
|
+
|
|
83
|
+
Si hay tensión entre dirección de audio y constraints técnicos (ej: música en stems para adaptatividad, pero el budget de memoria no da), surfacear el conflicto explícitamente con ambas posiciones. No resolver silenciosamente.
|
|
84
|
+
|
|
85
|
+
Si la dirección sonora conflictúa con accesibilidad (ej: información crítica solo por audio), surfacear y dejar que el usuario decida.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Principios
|
|
90
|
+
|
|
91
|
+
- El audio bible es un **documento de restricción**: narrows solution space en favor de coherencia sonora
|
|
92
|
+
- Cada sección debe conectar con los pilares del juego
|
|
93
|
+
- Específico > genérico: "dark and tense" no alcanza — decir la emoción exacta, la fuente tímbrica, el rango de frecuencia, un elemento sonoro que carga el mood
|
|
94
|
+
- Para cada referencia: qué tomar Y qué evitar
|
|
95
|
+
- El silencio es una decisión de diseño, no la ausencia de una — documentarlo
|
|
96
|
+
- Los standards deben ser producibles: un contractor externo debería poder entregar assets válidos sin briefing adicional
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Anti-patterns
|
|
101
|
+
|
|
102
|
+
> → Read references/anti-patterns.md for common mistakes to avoid
|
|
103
|
+
|
|
104
|
+
## Verdict
|
|
105
|
+
|
|
106
|
+
Al cerrar el documento, emitir un verdict por sección:
|
|
107
|
+
|
|
108
|
+
- **READY** — la sección resuelve ambigüedades y es accionable para producción
|
|
109
|
+
- **CONCERNS** — está escrita pero es genérica o no ancla en pilares; listar qué falta
|
|
110
|
+
- **BLOCKED** — falta input upstream (game concept, plataformas, budget) que no se puede inventar
|
|
111
|
+
|
|
112
|
+
Si alguna sección Core (1-4) queda en BLOCKED, el bible no habilita producción todavía.
|
|
113
|
+
|
|
114
|
+
## Next steps
|
|
115
|
+
|
|
116
|
+
- `/audio-spec` para bajar la dirección a spec sheets de SFX y event lists
|
|
117
|
+
- `/adaptive-music` para diseñar el sistema de música contra los emotional targets de la sección 2
|
|
118
|
+
- `/audio-mix` para implementar la mix hierarchy de la sección 5
|
|
119
|
+
- `/audio-audit` para verificar assets producidos contra los standards de la sección 8
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Audio Bible — Anti-patterns
|
|
2
|
+
|
|
3
|
+
## Dirección vacía
|
|
4
|
+
|
|
5
|
+
**Síntoma:** "El juego suena oscuro e inmersivo."
|
|
6
|
+
|
|
7
|
+
Cualquier sonido pasa ese filtro, así que no filtra nada. Una statement útil excluye: "Todo suena grabado dentro de una habitación de concreto — nada nace limpio, todo tiene el cuarto encima." Eso rechaza el 90% de una library.
|
|
8
|
+
|
|
9
|
+
## Paleta sin fuentes
|
|
10
|
+
|
|
11
|
+
Listar adjetivos (cálido, agresivo, etéreo) sin decir de dónde sale el sonido deja la decisión al sound designer, que es exactamente lo que el bible debería resolver. Nombrar fuentes: instrumentos, materiales, técnicas de grabación, cadenas de procesamiento.
|
|
12
|
+
|
|
13
|
+
## Frequency allocation ausente
|
|
14
|
+
|
|
15
|
+
Se produce todo, se llega al mix, y diálogo compite con música y SFX en 500 Hz-2 kHz. La solución tardía es EQ quirúrgico que adelgaza todo. La solución temprana es asignar bandas antes de producir.
|
|
16
|
+
|
|
17
|
+
## Copiar la referencia entera
|
|
18
|
+
|
|
19
|
+
"Suena como Hollow Knight" sin decir qué se toma produce pastiche. Para cada referencia: qué tomar y qué evitar explícitamente.
|
|
20
|
+
|
|
21
|
+
## Emotional targets que no se distinguen
|
|
22
|
+
|
|
23
|
+
Si exploración y tensión comparten instrumentación, tempo y densidad, el jugador no lee el estado por audio. Test: describir los estados sin nombrarlos y ver si son distinguibles.
|
|
24
|
+
|
|
25
|
+
## Standards no producibles
|
|
26
|
+
|
|
27
|
+
"Audio de alta calidad, sin artefactos" no es un standard. Sample rate, bit depth, formato por plataforma, naming pattern, target de loudness con tolerancia, headroom y budget de voces sí lo son.
|
|
28
|
+
|
|
29
|
+
## El silencio sin documentar
|
|
30
|
+
|
|
31
|
+
Los juegos con mejor audio usan silencio deliberadamente. Si el bible no dice dónde y por qué, alguien va a llenar cada segundo con ambiente.
|
|
32
|
+
|
|
33
|
+
## Ignorar la plataforma
|
|
34
|
+
|
|
35
|
+
Un bible que asume monitores de estudio falla en el speaker de un handheld o en earbuds bluetooth. Los targets de loudness y la allocation de frecuencias cambian por plataforma — documentar cuáles importan.
|
|
36
|
+
|
|
37
|
+
## Bible que nadie gatea
|
|
38
|
+
|
|
39
|
+
Si no hay un paso de review que compare assets contra el bible, es decoración. Conectarlo con `/audio-audit` y hacerlo condición de entrega.
|