Visión general
Entrada: el usuario introduce dimensiones / forma / materiales, posiciones de altavoces y de escucha — o la geometría se mide con la cinta métrica AR (sin LiDAR) o se captura con un escaneo LiDAR (RoomPlan).
- Localización de reflexiones tempranas
- RT60 (medido + simulado) más EDT / C50 / C80 / D50 (derivados de la RIR simulada)
- Lista de frecuencias modales
- Plan de paneles (posición, cantidad, SKU)
- Visualización 3D + mapa de energía de reflexión
- Exportación PDF nivel manual de instalación
- Medición in situ por barrido ESS / palmada (micro integrado o micro USB de medición)
- Calibración de micro USB de medición y referencia SPL, espectro en tiempo real (RTA)
- Superposición de respuestas y promedios multiposición, interoperabilidad REW, PEQ / alineación de subwoofer, asistente de IA
Se admiten salas rectangulares, en L, en U y polígonos simples arbitrarios; las reflexiones de 1er orden y el cálculo de área / pertenencia usan el polígono real; los órdenes 2/3 y los modos aproximan con la caja envolvente (véase §9).
Modelo acústico
2.1 Sabine RT60
La fórmula clásica de Sabine:
- Volumen (m³); para polígonos, área de Shoelace × altura, menos obstáculos (columnas / vigas).
- Superficie × coeficiente de absorción por cara. Coeficientes en 6 bandas (125 / 250 / 500 / 1k / 2k / 4k Hz).
- = 24·ln(10)/c, con c = 343 m/s (20 °C).
La app incluye 4 preajustes de acabado (sala vacía y dura / acabado duro típico / mobiliario / ya tratado), cada uno con curva por banda; la estimación RT60 de valor único y el presupuesto de paneles usan un coeficiente medio de medios-agudos, mientras la curva Sabine de 6 bandas alimenta la vista por banda. El RT60 objetivo también es una curva de 6 bandas, acorde a normas como EBU Tech 3276 / THX que permiten una cola más larga en graves.
2.2 Método de fuentes imagen (ISM)
Las primeras reflexiones usan espejo: se refleja la fuente respecto al muro; la intersección del segmento «fuente espejo → oyente» con el muro es el punto de reflexión.
- Sala rectangular: espejo en los 6 planos — solución cerrada.
- No rectangular (
PolygonReflectionCalculator): espejo en cada segmento deRoomPolygon.walls()y validar s ∈ [0, 1] (las reflexiones de suelo / techo exigen además el XY dentro del polígono); las rutas de 1er orden pasan también una prueba de oclusión 2D: cualquier tramo fuente→punto→oyente que cruce otro segmento de muro se descarta.
Las reflexiones multi-orden (1–3) las implementa MultiOrderReflectionCalculator según el enfoque recursivo de pyroomacoustics:
- Enumerar todas las secuencias de muros [s₁, …, sₙ], muros adyacentes distintos
- Iteración de fuente imagen: S₀ = fuente, Sₖ = mirror(Sₖ₋₁, sₖ)
- Trazado inverso desde el oyente: Sₙ → oyente corta sₙ en Pₙ; Sₙ₋₁ → Pₙ corta sₙ₋₁ en Pₙ₋₁; y así sucesivamente
- Cada intersección debe estar en el rango válido del muro y t ∈ (0, 1)
2.3 Energía de trayectos
Energía de banda ancha según número de reflexiones y atenuación por distancia:
- α se acota a [0, 0.99] para que α = 1 no produzca log(0) ni pow(negativo, N) = NaN
- La atenuación sigue la divergencia esférica 1/r²
- Conversión a dB con 10·log₁₀ (dominio de energía)
La energía por banda se calcula para las 6 bandas (coeficiente de finishProfile.absorptionByBand).
2.4 Estimación de RT60 (medida)
ImpulseResponseEstimator:
- Filtrado adaptado: el barrido grabado se filtra en inverso (ESS / Farina) hasta un impulso; ganancia de proceso ≈ 10·log₁₀(T·B) — unos +41 dB de SNR efectiva con el preajuste de teléfono de 3 s, 180 Hz–4 kHz
- rmsEnvelope: envolvente RMS de ventana deslizante
- Integración inversa de Schroeder: E_back(t) = ∫ₜ^∞ s²(τ) dτ, normalizada con 10·log₁₀ da la curva EDC
- Regresión lineal de pendiente: T30 [−5, −35 dB] según SNR, con respaldo T20 [−5, −25] o T10 [−5, −15]
- RT₆₀ = −60 / pendiente
RT60 por banda: sobre las muestras filtradas, paso de banda en los centros de octava ISO elegidos según el rango de barrido real (Q = √2) y Schroeder por banda.
2.5 Simulación de RIR
SimulatedRIRGenerator convierte las rutas multi-orden en impulsos discretos escritos en un búfer PCM de 8 kHz × 0,5 s:
for path in reflectionPaths:
t = path.totalDistance / speedOfSound
amp = 10^(path.relativeEnergyDB / 20)
impulse[round(t * sampleRate)] += amp
Añadir el impulso del sonido directo y aplicar integración de Schroeder para EDT / RT60 / C50 / C80 / D50.
2.6 Modos propios
Frecuencias modales de una sala rectangular:
Clasificados según cuántos de nₓ, n_y, n_z son no nulos: 1 = axial, 2 = tangencial, 3 = oblicuo.
La enumeración cubre n ≤ 4 por eje hasta 300 Hz. Las salas no rectangulares estiman los modos con la caja envolvente (los modos exactos requieren FEM); se indica en la UI.
Sistema geométrico
3.1 Formas admitidas
- Rectángulo — ancho × largo × alto
- Forma en L — quita una muesca nw × nl en una esquina
- Forma en U — quita una muesca nw × nl en el centro de un muro
- Personalizado — cualquier polígono simple (≥ 3, ≤ 24 vértices)
Todas las formas no rectangulares pasan a una lista de vértices CCW vía RoomPolygon.from(shape:, bboxWidth:, bboxLength:). Las reflexiones de 1er orden, área / perímetro y pertenencia usan estos vértices directamente; un polígono personalizado inválido (< 3 / > 24 vértices, autointersecado, área casi nula) degrada en silencio al rectángulo envolvente.
3.2 Utilidades de geometría poligonal
- Área — fórmula de Shoelace
- Perímetro — suma de longitudes de aristas
- contains(x, y) — ray-casting con epsilon de borde (evita falsos negativos en esquinas)
- clamped(point) — los rectángulos usan la vía rápida [0, w] × [0, l]; los demás el verdadero [minₓ, maxₓ] × [min_y, max_y] de
polygon.boundingBox, prueban polygon.contains y búsqueda binaria hacia el centro si está fuera - walls() — divide el polígono en segmentos clasificados por normal interior (leftWall / rightWall / frontWall / backWall), reutilizando el recomendador rectangular
- PolygonValidator.isSimple — valida un polígono simple sin autointersección
3.3 Escaneo RoomPlan
Se usa RoomCaptureSession (API de iOS 16+; la app exige iOS 17 para escanear) y CapturedRoomMapper mapea el escaneo a ScannedRoom (wallSegments + openings + furniture). Mapeo de coordenadas:
El escaneo se clasifica por número de muros: exactamente 4 segmentos se tratan directamente como rectángulo; el resto pasa por RectangularRoomSimplifier.polygonShapeFromScan, encadenando wallSegments en vértices. Los extremos de RoomPlan erran 3–8 cm; un bucle de 12 segmentos puede acumular 5 cm, por eso el epsilon de cierre es 12 cm. Si no cierra, recurre a la caja envolvente y pide reescanear.
3.4 Cinta métrica AR
Sin LiDAR, ARTapeMeasureView (ARView de RealityKit con seguimiento de mundo) toma el relevo: apunta la retícula a la pared y toca dos veces para marcar inicio / fin, siguiendo ancho → largo → alto, y confirma para generar una sala rectangular — con guía de permiso de cámara y avisos de fallo de seguimiento.
3.5 Editor de vértices libres
FreeVertexEditorView permite arrastrar vértices en un Canvas de SwiftUI:
- Mantén pulsado para borrar (con > 3 vértices, para que queden ≥ 3)
- Cada frame de arrastre se valida con
isSimple; una posición con autointersección simplemente no se aplica (el vértice se queda quieto). Tocar un área vacía inserta un vértice en la arista más cercana; hay además un editor numérico por vértice y un botón de restablecer al rectángulo envolvente - Ajuste a rejilla de 0,05 m
- Un margen de max(0,5, bbox × 0,2) m fuera de la bbox permite arrastrar vértices más allá (la vista padre agranda room.width / length)
Zonas de escucha y multi-oyente
AcousticConfiguration.listeners admite 1..N posiciones de escucha (con 0 oyentes se sintetiza una posición principal por defecto con aviso; con 0 altavoces toman el relevo las plantillas del §5.2). El análisis ejecuta el método de imágenes por oyente y fusiona los puntos en un array (el oyente principal primero). El recomendador pondera cada muro por su número de puntos (cap = max(6, speakerCount × listenerCount)); varios asientos compartiendo un muro lo empujan al tope del presupuesto.
- En 3D, los oyentes secundarios: opacidad 0,45 + escala 0,75
- Vista superior (RoomPlanView): puntos primarios a todo color r=5, secundarios opacidad 0,45 r=3
- La lista (RecommendationView) muestra solo puntos primarios; nota al pie «+ N reflexiones ya incluidas»
Las reflexiones multi-orden / RIR / mapa se calculan una vez para el oyente principal (O(speakers × surfaces³) por oyente es demasiado, y el RIR es de un solo punto).
Motor de recomendación
PanelPlacementRecommender produce [PanelRecommendation], cada uno con: posición (muro izq. / der. / techo / frontal / trasero / esquina), cantidad, prioridad (esencial / fuerte / recomendado / opcional), effectivenessMultiplier, targetReflectionPointId.
5.1 Reparto proporcional (mayor resto)
El antiguo add() voraz dejaba muros sin nada: el izquierdo agotaba el presupuesto → el derecho 0; con presupuesto ajustado también techo / frontal / trasero. La nueva versión usa el método del mayor resto:
- Calcular el «raw desired» por muro (nº de puntos + profundidad + caso de uso)
- floor(raw × budget / total) como asignación inicial
- los muros con raw > 0 reciben ≥ 1 panel; solo cuando el presupuesto es menor que el número de muros con demanda se ponen superficies a cero, primero la prioridad más baja
- si la suma > presupuesto, recortar desde el menor resto fraccionario
- pase de simetría izq./der.: si raw[0] == raw[1], forzar alloc[0] == alloc[1]
- redistribuir el sobrante según la mayor fracción
5.2 Plantillas por caso de uso
Sin altavoces colocados, se aplican plantillas por caso de uso:
- Sala de reuniones: techo 35 % + muro trasero 20 % + cada muro lateral 12 %
- Pódcast: justo frente al micro 25 % + cada lado 18 % + techo 20 %
- Estudio: amortiguación simétrica en 4 muros + hasta 4 trampas de graves en esquina dentro del presupuesto (el verdadero salto del presupuesto solo ocurre en la vía de modos graves densos del §5.3)
- Escucha general: base simétrica + aviso para añadir altavoces
5.3 Las necesidades físicas saltan el presupuesto
Las trampas de graves de esquina se fuerzan a 4 cuando los modos 80–150 Hz son densos, saltando el tope totalPanel. Razón: en las esquinas la presión es máxima y las trampas son ~1,5× más eficientes — necesidad física.
5.4 Ponderación cercana al oyente
Una pasada final marca las recomendaciones a menos de 1,5 m de un oyente como .nearListener, effectivenessMultiplier ×1,15, y lo indica en el título / motivo.
Medición
6.1 Barrido ESS
Barrido logarítmico ESS (método de Farina): cuatro preajustes + personalizado, duración de 3 / 6 / 12 s:
- Prueba rápida con el teléfono: 180 Hz–4 kHz por la salida integrada, 75 Hz–4 kHz por salida externa (único preajuste que admite el altavoz integrado)
- Graves / subwoofer: 15–300 Hz (requiere salida externa)
- Banda completa: 20 Hz–20 kHz (requiere salida externa)
- Personalizado: límites libres dentro de 10 Hz–20 kHz cubriendo al menos una octava (requiere salida externa)
- Filtro inverso (
makeInverseSweep) = inversión temporal + envolvente de amplitud de +6 dB/octava (creciente con la frecuencia, para compensar el espectro rosa del ESS) - Grabación con AVAudioRecorder a Linear PCM sin pérdida (mono 16 bits .caf, ~96 dB de rango dinámico — los códecs perceptuales no sirven para medir impulsos; la tasa sigue la sesión de audio — 44,1 kHz con el micro integrado, 48 kHz nativos con micros de medición USB)
- Lectura → filtrado adaptado → analyzeImpulse
La señal de reproducción, el barrido inverso y el análisis comparten una misma configuración, con la frecuencia máxima limitada por la tasa de muestreo real; los preajustes de graves / banda completa / personalizado no arrancan sin salida externa. Las bandas de octava de RT60 se eligen según el rango de barrido real; el rango de un preajuste es la configuración del estímulo — el rango fiable sigue dependiendo del altavoz, el micrófono y la calidad de la medición.
makeInverseSweep(sampleRate:) está parametrizado) — el filtrado adaptado siempre está activo, tanto a 44,1 kHz (micro integrado) como a 48 kHz (micros de medición USB).6.2 Impulso de palmada
Sin reproducción — el usuario da una palmada fuerte dentro de una ventana de 1 s; se graban 4,5 s en total (~3,5 s de cola). Va directo a analyzeImpulse (sin filtro adaptado; una palmada ya es un impulso estrecho). Cubre la banda de 125 Hz que el preajuste de teléfono (desde 180 Hz) no alcanza.
6.3 Medido vs simulado
MeasuredVsSimulatedCard toma el último RT60 medido y muestra la desviación frente al RT60 simulado:
6.4 Ruido ambiente y calibración
AmbientNoiseEstimator graba 3 s de silencio (recortando 100 ms en cada extremo) para el ruido de fondo: media / pico dBFS de banda ancha + valoración de silencio en 5 niveles (dBFS solo relativo hasta una calibración SPL, tras la cual se muestra dB SPL). La calibración de latencia de campo cercano reutiliza el mismo barrido ESS: teléfono a menos de 30 cm del altavoz, retardo inicio de grabación → pico del impulso (con un pre-roll deliberado de 250 ms), idealmente 2–3 pasadas — dispersión de las últimas 3 lecturas < 5 ms = confianza alta, < 20 ms = media. La latencia queda ligada al par «entrada + ruta de salida» actual, se invalida si cualquiera cambia y las mediciones lejanas muestran el retardo de propagación ya corregido.
6.5 Referencia de tiempo y calidad de medición
- Referencia de tiempo acústica: un chirp precede al barrido como ancla temporal, redetectada en cada medición; si la detección falla, se recurre al modo de reserva marcando explícitamente la fiabilidad de fase — sin pretender alineación absoluta entre mediciones.
- Deriva de reloj: estima la diferencia de reloj entre las rutas de reproducción y grabación y la corrige cuando las condiciones lo permiten.
- Referencia de loopback eléctrico: las interfaces USB multientrada pueden usar un segundo canal como referencia loopback; si no se cumplen las condiciones, se recurre a la referencia acústica. La ruta del asistente de reproducción para Mac no usa el loopback eléctrico local.
- Campos de calidad: cada registro guarda la SNR, el ruido de fondo, el estado de recorte, el offset temporal del IR, la entrada real y la tasa de muestreo disponibles; lo indeterminable se deja vacío, nunca se inventa.
- Gestión del recorte: un barrido con recorte detectado se invalida y se pide repetir la medición; los registros de palmada se marcan y avisan.
- Contraste simulación / medición: la última medición de la sala se compara en paralelo con el RT60 de la simulación geométrica para evaluar el ajuste del modelo (umbrales en 6.3).
6.6 Micros USB de medición y enrutado de salida
El selector de entrada de la página de medición lista el micro integrado y las entradas USB class-compliant (p. ej. miniDSP UMIK-1 / UMIK-2); una escucha de routeChangeNotification vuelve al micro integrado si el externo desaparece. Las tasas de grabación y barrido siguen la sesión (UMIK-1 = 48 kHz). Los archivos de calibración de frecuencia .txt estilo REW / UMIK se importan directamente (leyendo Sens Factor y el número de serie, interpolación en frecuencia logarítmica; se recomienda el archivo de 90° para medición de salas). La calibración queda ligada al UID del puerto de entrada — con una entrada que no coincide, la calibración anterior no se aplica.
Calibración SPL de referencia única: coloca un calibrador de nivel (94 dB @ 1 kHz por defecto) sobre el micro, la app muestrea 3 s de nivel para derivar el offset dBFS → SPL; los dB SPL solo se muestran si la entrada coincide y existe una calibración SPL — si no, las lecturas quedan en dBFS. Instantánea de calibración en el momento de la medición: los nuevos registros de barrido / palmada guardan la curva de calibración, el nombre, el número de serie, el Sens Factor, el offset SPL y el UID de entrada vigentes entonces — sustituir, borrar o revincular la calibración después no cambia las lecturas de esos registros; el detalle de medición, la vista previa de respuesta tras la ventana de IR, la exportación de texto de la respuesta y el PDF usan la misma instantánea. La calibración en sí se persiste por UID de puerto de entrada en ~/Library/Application Support/mic_calibration.json.
La reproducción pasa por el selector de ruta del sistema (wrapper de AVRoutePickerView, en las tarjetas de barrido / calibración de campo cercano); la sesión permite AirPlay / Bluetooth A2DP / HFP para emitir los tonos por altavoces externos, con altavoz forzado si no hay. La entrada de grabación siempre queda fijada explícitamente a la elección del usuario (micro USB o integrado) para que el micro de un dispositivo Bluetooth no robe la entrada durante la reproducción y contamine la medición. Asistente de reproducción para Mac: descubre AcouveroMacSpeaker en la LAN vía Bonjour, transfiere primero el WAV de barrido al Mac y luego envía la orden de reproducción, mientras el iPhone graba — para rutas de salida de Mac que no pueden alojar directamente el audio de la sesión de medición (esta ruta no usa el loopback eléctrico local).
6.7 Interoperabilidad con REW y archivos
- Importación / exportación de texto de respuesta: compatible con separadores comunes, líneas de comentario, notación científica y formatos decimales, validando el eje de frecuencias al importar; los archivos sin columna de fase no generan datos de fase.
- Exportación consciente de la calibración: exporta dB SPL si existe una instantánea de calibración SPL válida, dBFS en caso contrario — la cabecera etiqueta la unidad con honestidad.
- Importación de audio IR: se admiten respuestas al impulso WAV / AIFF; los archivos multicanal exigen elegir el canal explícitamente.
- Análisis de barrido sin conexión: exporta el archivo de estímulo para reproducción externa y, tras importar la grabación, hay que indicar las frecuencias de inicio / fin y la duración antes de la deconvolución — los parámetros del barrido nunca se adivinan.
- Exportación para compartir audio: los IR o grabaciones brutas disponibles se pueden compartir como WAV.
- Los resultados importados pueden archivarse y entrar en los gráficos, superposiciones y análisis aplicables; si pueden unirse a un grupo de medición depende del tipo de datos y del alcance de confianza. Los archivos de proyecto REW
.mdatno se leen por ahora.
Gráficos y análisis de medición
La página de detalle muestra gráficos según los datos que realmente tiene cada registro — nunca se inventan curvas ausentes; los gráficos admiten modo claro / oscuro, Dynamic Type y resúmenes de accesibilidad.
- RT60: gráfico de barras por banda de octava con línea objetivo y colores de estado.
- Decaimiento de Schroeder: curva EDC con el intervalo de ajuste T20.
- Respuesta en frecuencia: eje logarítmico, suavizado por octava y lectura con cursor; se muestra en dB SPL mientras la instantánea de calibración sea válida.
- Respuesta al impulso: forma de onda con marcadores de cero temporal y de pico; amplitud relativa al pico.
- Fase: vistas wrapped / unwrapped, inversión de polaridad, ajuste fino del cero temporal e indicación de fiabilidad de la referencia.
- Retardo de grupo: retardo de grupo en la banda válida, con análisis opcional de fase mínima / fase en exceso.
- Ventana de IR: referencia de pico o t=0, anchos izquierdo / derecho independientes, rectangular / Hann / Tukey / Blackman; respuestas original y enventanada en vista previa lado a lado — la ventana y el ajuste de fase solo afectan a la vista previa, nunca a la medición guardada.
- Espectrograma: decaimiento tiempo-frecuencia del IR, con rango temporal y dinámico ajustables.
- Cascada y RT60 Decay: visualización del decaimiento de graves, longitud de ventana / tiempo / rango dinámico ajustables; los ajustes de calidad insuficiente no se muestran.
- Espectro en tiempo real (RTA): visualización de 1/1–1/24 de octava, ponderación A / C / Z, respuesta instantánea / Fast / Slow, retención y borrado de picos, nivel global, nivel de pico y frecuencia dominante.
- El RTA usa la calibración de la entrada correspondiente y se etiqueta dBFS hasta que exista una calibración SPL; es mutuamente excluyente con las mediciones formales (al entrar libera la escucha de nivel de la página de medición, al cerrar la restaura), y las retrollamadas antiguas nunca sobrescriben el estado nuevo tras parar o cambiar de entrada.
Historial, promedios multiposición y superposición
- Archivo V2: las mediciones guardan metadatos, la grabación bruta / sidecar de IR disponible y los datos de análisis; los registros antiguos siguen siendo compatibles en solo lectura, sin inventar los datos brutos ausentes.
- Gestión del historial: ver y borrar mediciones por sala, abrir el detalle o elegir registros para análisis posteriores.
- Comparación antes / después: elige dos registros para comparar RT60 global y por banda, respuestas superpuestas y diferencias en las bandas válidas.
- Superposición de 2–6 respuestas: suavizado de 1/3–1/48 de octava, mostrar / ocultar por curva, preajustes de rango banda completa / graves; niveles sin alinear, puestos a cero en medios o alineados a la primera curva, con offsets en la leyenda; los datos de origen intactos, y los registros no superponibles explican por qué.
- Grupos de medición: los registros llevan etiqueta de posición, pesos y etapa antes / después; crea un grupo desde el historial, únete a uno, o mide una nueva posición directamente en un grupo.
- Promedios multiposición: media aritmética en dB, media de potencia RMS y — con referencia de tiempo fiable y espectros complejos — media compleja vectorial; muestra la curva media, las curvas miembro y el rango espacial min / max por frecuencia.
- Comparación de grupos: compara las respuestas medias, los rangos espaciales y los RT60 medios de dos grupos, con conclusión sobre la consistencia espacial.
PEQ y alineación de subwoofer
9.1 Ajuste de objetivo PEQ
- Curva objetivo parametrizada: nivel objetivo, realce de graves, caída de agudos y banda de ajuste.
- Restricciones de filtros: número de filtros, ganancia / atenuación máxima y rango de Q.
- Predicción y justificación: medición, objetivo, respuestas de los filtros y resultado previsto en un mismo gráfico, con métricas de mejora y justificación filtro a filtro.
- Exclusión de caídas profundas: las caídas profundas por cancelación acústica se excluyen automáticamente, evitando grandes ganancias sobre un nulo.
- Exportación: texto Generic, Equalizer APO, coeficientes miniDSP y YAML de CamillaDSP — solo cálculo y exportación de ajustes; nada se escribe directamente en los dispositivos.
Aún no hay editor manual de filtros, biblioteca de objetivos ni archivo de resultados de EQ.
9.2 Alineación de dos vías y multi-subwoofer
- Alineación de dos vías: elige una medición del altavoz de referencia y otra del subwoofer ajustable, predice la suma compleja y resuelve retardo, ganancia y polaridad; la curva sumada se muestra antes y después de alinear.
- Multi-sub, multiasiento: 1–4 subwoofers con mediciones independientes elegidas por asiento × fuente; optimización para todos los asientos, limitada por el peor asiento, o explícitamente solo el asiento principal.
- Resultados y compartición: ajustes por sub, mejora por asiento, métricas medias y de peor caso, con ajustes compartibles como texto.
- Requisitos de datos: los registros sin configuración de barrido, referencia de tiempo fiable o IR indican el motivo de su no disponibilidad en lugar de excluirse en silencio.
Los resultados multiasiento son por ahora una tabla numérica sin superposición de curvas sumadas por asiento; la alineación es una predicción que debe verificarse remidiendo tras aplicar los ajustes; mezclar fuentes con distintas tasas de muestreo está aún por validar.
Asistente acústico de IA
La barra de herramientas del historial de mediciones ofrece una entrada de chat que usa la propia clave de API de Anthropic del usuario (guardada en el Keychain del dispositivo); el asistente envía a Anthropic la conversación y los datos de herramientas necesarios para cada solicitud, y los costes de API corren a cargo de la cuenta del usuario.
- Interfaz de conversación: respuestas en streaming, estado de ejecución de herramientas, detener y vaciar, estimación de uso / coste.
- Lectura de datos de medición: listar registros, resúmenes y respuestas parciales, y comparar mediciones.
- Herramientas de cálculo: invoca exactamente los mismos algoritmos de recomendación PEQ y alineación de subwoofer que la interfaz manual.
- Evaluación de sala y disposición: lista salas, lee posiciones de altavoces / oyente y modos, y predice — sobre una copia del proyecto — el efecto de mover equipos o cambiar el perfil de absorción; los proyectos guardados nunca se reescriben.
- Medición solo tras confirmación: la IA propone el propósito y las salas candidatas y solo inicia el barrido tras la confirmación del usuario; rechazar o detener cancela la solicitud.
- Ajustes de medición compartidos: la IA y la medición manual reutilizan el mismo espacio de trabajo, heredando entrada, asistente de reproducción para Mac, rango / duración de barrido, retardo y loopback; la página de chat muestra nivel / progreso en vivo con cancelación, y al terminar restaura el contexto original de sala y grupo de medición.
- Restauración de sesión: se guarda y restaura una sesión actual, marcando al restaurar la ejecución de herramienta interrumpida; vaciar la sesión borra también el historial y el uso.
Visualización y salida
11.1 Vista 3D
Room3DController renderiza con SceneKit: muros / suelo / techo / altavoces (caja fina + marca + píldora de canal) / oyente (glifo FE + tallo) / obstáculos (SCNBox translúcido) / paneles recomendados / segmentos de rutas multi-orden / superposición de mapa de energía.
11.2 Mapa de energía de reflexión
ReflectionEnergyMapGenerator acumula la energía de cada ruta en la rejilla uCells × vCells del muro (12×6 por defecto). Un mapa por muro, superpuesto en 3D (azul → cian → verde → amarillo → rojo).
EnergyHeatmapRenderer convierte la rejilla en UIImage: celdas «block» de 16 px (sin interpolación entre celdas, para que los bordes sigan legibles; el suavizado en el muro viene del filtrado bilineal de textura de SceneKit) + rampa propia de 5 tramos (azul → cian → verde → amarillo → rojo) + alfa mapeado linealmente de 0,20 a 0,85 según la posición de la energía en una escala fija de −30…0 dB (−∞ totalmente transparente).
11.3 Vista previa por convolución RIR
RIRPreviewPlayer (AVAudioEngine + AVAudioPlayerNode) convoluciona una señal seca sintetizada (cuerda pulsada / palmada / voz vocálica sintética — sin muestras incluidas) con el RIR simulado para oír «sin tratar vs tratado».
Convolución temporal RIRConvolver.convolve(dry:rir:) directa O(N × M) (N = muestras secas, M = RIR), normalizada a pico 0,9 para evitar recorte.
11.4 Manual de instalación PDF
ReportExporter genera un PDF A4 (595 × 842 pt) con UIGraphicsPDFRenderer:
- Resumen de sala + captura 3D
- Gráfico de barras RT60 de 6 bandas: estimación Sabine vs objetivo, con valores medidos superpuestos cuando existen
- Tabla de puntos + tabla de modos + resumen de riesgos
- Tabla de paneles (la lista de compra BOM se muestra en la app, no en el PDF)
- Cuando hay datos de medición, se añaden gráficos de RT60 / EDC / respuesta / espectrograma / cascada / fase y un resumen de promedio multiposición; la unidad de la respuesta usa la instantánea de calibración guardada al medir (etiquetada con honestidad como dB SPL o dBFS)
- Fuente Geist + respaldo PingFangSC (CoreText cascadeList, para que el CJK no salga como □□□)
11.5 Persistencia de proyectos
ProjectStore (actor) escribe ~/Library/Application Support/Acouvero/projects.json: [RoomProject], cada uno con Room + altavoces + oyentes + recomendaciones + panelCountOverrides. En el primer arranque, los datos migran automáticamente del directorio antiguo FindEcho/ (FindEcho es el nombre en clave interno previo al renombrado, conservado a propósito en los símbolos del código).
MeasurementStore (actor) escribe measurements.json: [MeasurementResult], vinculado al proyecto por roomId.
Ambos usan recuperación de decodificación por elemento (envoltorio Throwable<T>) — un registro corrupto no hace fallar toda la lista.
MicCalibrationStore escribe ~/Library/Application Support/mic_calibration.json (raíz, sin subdirectorio): curva de calibración REW + offset SPL, ligados al UID del puerto de entrada — cambiar de entrada exige reimportar / recalibrar.
Notas de ingeniería
12.1 SwiftUI + @Observable
El ViewModel (FindEchoViewModel) usa la macro @Observable de Swift 5.9 — campos observados directamente, sin Combine. Los resultados de AcousticAnalysis se cachean por comparación Equatable de AcousticConfiguration:
var analysis: AcousticAnalysis {
let cfg = configuration
if let cached = cachedAnalysis, cachedConfiguration == cfg {
return cached
}
let result = analysisService.analyze(cfg)
cachedConfiguration = cfg
cachedAnalysis = result
return result
}
12.2 Aislamiento por actor
ProjectStore y MeasurementStore son actors, que serializan IO / async de forma natural; RoomPlanService es una clase @MainActor. La UI usa async / await; el hilo principal nunca se bloquea por IO de disco.
12.3 Ciclo de vida de AVAudio
AudioSessionManager centraliza la categoría de sesión (.playback para la vista previa RIR, .playAndRecord para la medición). La protección contra interrupciones cubre ambas rutas: en medición, un InterruptionMonitor dentro de AudioMeasurementService escucha AVAudioSession.interruptionNotification — una llamada / Siri durante el barrido, la palmada o el ruido ambiente descarta el resultado con error; en la vista previa, RIRPreviewPlayer también escucha y detiene el nodo de reproducción limpiando isPlaying. Un cambio de ruta de audio a mitad de medición también invalida la pasada).
12.4 Cancelación de tareas
La reproducción por convolución es una tarea de fondo; los toques rápidos disparan pendingPlayTask?.cancel() más una doble verificación Task.isCancelled para evitar escrituras desordenadas en MainActor.
12.5 Cobertura de pruebas
AllShapesAndUseCasesTests usa el framework Swift Testing para la matriz completa — cada forma × caso de uso (oyente único; altavoces en par estéreo o uno central según el caso) — y comprueba:
analysis.reflectionPaths.count >= 1analysis.warningsincluye la nota de aproximación para salas no rectangulares- el total de paneles recomendados es no negativo (totalPanelCount ≥ 0; sin aserción de presupuesto)
- los polígonos autointersecados degradan con seguridad (sin crash, a rectángulo)
- vértices < 3 y otros retrocesos de casos límite
12.6 Plataforma y localización
Destino de despliegue iOS 17.0, iPhone + iPad; cero dependencias de terceros (sin paquetes SPM / CocoaPods) — solo frameworks del sistema (SwiftUI / SceneKit / ARKit / RoomPlan / AVFoundation / Accelerate). Localización con catálogo .xcstrings: chino como lengua fuente más English / Français / Deutsch / Español / Português (Brasil) — 6 idiomas, conmutable en la app o siguiendo al sistema.
Limitaciones conocidas
- Multi-orden en salas no rectangulares usa la caja envolvente — el riesgo de flutter / slapback de 2.º/3.º orden cerca de esquinas cóncavas puede desviarse. Los puntos de 1.er orden usan el polígono real.
- Modos en salas no rectangulares estimados por caja envolvente — modos exactos requieren FEM.
- Medición con el micrófono del iPhone — sin calibrar; respuesta / sensibilidad no planas. Fiabilidad siempre
.low, como contraste con la estimación geométrica. Con un micro de medición USB (miniDSP UMIK-1 / UMIK-2) y su archivo de calibración REW cargado, los niveles por banda se corrigen según la curva de calibración; una calibración SPL de referencia de 94 dB muestra después los niveles ambiente y de medición como dB SPL absolutos (solo dBFS hasta entonces). - Supuesto ISM de fuente única — ignora difracción de caja, directividad de bocina, etc.
- Sin modelado de trampas de graves — los problemas bajo 80 Hz quedan fuera del modelo de Sabine y requieren tratamiento dedicado.
- Precisión del escaneo AR — extremos de RoomPlan con error de 3-8 cm; un bucle de 12 segmentos puede acumular 5+ cm. No se modela el grosor de los muros.
- Límites del alcance de análisis — el promedio complejo vectorial entre tasas de muestreo aún no se admite; los resultados de alineación multiasiento son por ahora una tabla numérica sin superposición de curvas sumadas por asiento; las superposiciones de respuesta aún no entran en el PDF.
- Herramientas profesionales aún no implementadas — sin registro SPL a largo plazo, inteligibilidad STI ni medición de distorsión (THD); impedancia / Thiele-Small y API REST tampoco existen.
- Aceptación en dispositivo pendiente — la cadena de API real del asistente de IA (streaming con clave real / medición confirmada / archivo de grabaciones) y la cadena completa de micros UMIK en hardware real (incluido el UMIK-2 a 96 kHz) están pendientes; pasar en simulador no prueba el bucle de audio en dispositivo.
Referencias
- Sabine, W. C. (1922). Collected Papers on Acoustics. Harvard University Press.
- Allen, J. B., & Berkley, D. A. (1979). Image method for efficiently simulating small-room acoustics. JASA, 65(4), 943–950.
- Schroeder, M. R. (1965). New method of measuring reverberation time. JASA, 37(3), 409–412.
- Farina, A. (2000). Simultaneous measurement of impulse response and distortion with a swept-sine technique. Audio Engineering Society Convention 108.
- Scheibler, R., Bezzam, E., & Dokmanić, I. (2018). Pyroomacoustics: A Python package for audio room simulation and array processing algorithms. ICASSP 2018.
- EBU Tech 3276. Listening conditions for the assessment of sound programme material.
- ITU-R BS.1116. Methods for the subjective assessment of small impairments in audio systems.
- BB93 — Acoustic design of schools (UK).
- THX Pro Cinema Certification requirements.